From 7b2b9cb4f6ef3747a5d6c020e1eaefdbcda68cb7 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 12:12:38 +0800 Subject: [PATCH 01/21] rename the config-option-type.ts to just types.ts we are going to havec more types def inside and use it to create our type defintion file to ship with this module --- package-lock.json | 447 ++++++++++++++++++++ package.json | 1 + src/lib/base-tools.ts | 5 +- src/lib/index.ts | 2 +- src/lib/{config-option-type.ts => types.ts} | 2 +- src/provider/gitee/verify.ts | 11 +- 6 files changed, 464 insertions(+), 4 deletions(-) rename src/lib/{config-option-type.ts => types.ts} (88%) diff --git a/package-lock.json b/package-lock.json index 032be3d..122b443 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "esbuild": "^0.12.0", "esbuild-register": "^2.5.0", "eslint": "^7.26.0", + "supertest": "^6.1.3", "ts-node": "^9.1.1", "typedoc": "^0.20.36", "typescript": "^4.2.4" @@ -710,6 +711,12 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -1026,6 +1033,19 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1287,12 +1307,30 @@ "node": ">=0.1.90" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1386,6 +1424,12 @@ "node": ">= 4" } }, + "node_modules/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -1571,6 +1615,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -2056,6 +2109,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, "node_modules/fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -2155,6 +2214,29 @@ "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -2207,6 +2289,20 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -2394,6 +2490,18 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -3171,6 +3279,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -3184,6 +3301,39 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "dev": true, + "dependencies": { + "mime-db": "1.48.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", @@ -3289,6 +3439,15 @@ "node": ">=8" } }, + "node_modules/object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3859,6 +4018,21 @@ "node": ">=8" } }, + "node_modules/qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4384,6 +4558,20 @@ "vscode-textmate": "^5.2.0" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -4597,6 +4785,61 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 7.0.0" + } + }, + "node_modules/superagent/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/superagent/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/superagent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/supertap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supertap/-/supertap-2.0.0.tgz", @@ -4631,6 +4874,19 @@ "node": ">=8" } }, + "node_modules/supertest": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.1.3.tgz", + "integrity": "sha512-v2NVRyP73XDewKb65adz+yug1XMtmvij63qIWHZzSX8tp6wiq6xBLUy4SUAd2NII6wIipOmHT/FD9eicpJwdgQ==", + "dev": true, + "dependencies": { + "methods": "^1.1.2", + "superagent": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5763,6 +6019,12 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -6000,6 +6262,16 @@ } } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6192,12 +6464,27 @@ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6275,6 +6562,12 @@ "integrity": "sha1-fj5Iu+bZl7FBfdyihoIEtNPYVxU=", "dev": true }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -6414,6 +6707,12 @@ "slash": "^3.0.0" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -6781,6 +7080,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, "fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -6857,6 +7162,23 @@ "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true + }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -6896,6 +7218,17 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -7032,6 +7365,12 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, "has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -7598,6 +7937,12 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -7608,6 +7953,27 @@ "picomatch": "^2.2.3" } }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true + }, + "mime-db": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "dev": true, + "requires": { + "mime-db": "1.48.0" + } + }, "mimic-fn": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", @@ -7692,6 +8058,12 @@ "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8107,6 +8479,15 @@ "escape-goat": "^2.0.0" } }, + "qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8468,6 +8849,17 @@ "vscode-textmate": "^5.2.0" } }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -8629,6 +9021,51 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "supertap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supertap/-/supertap-2.0.0.tgz", @@ -8656,6 +9093,16 @@ } } }, + "supertest": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.1.3.tgz", + "integrity": "sha512-v2NVRyP73XDewKb65adz+yug1XMtmvij63qIWHZzSX8tp6wiq6xBLUy4SUAd2NII6wIipOmHT/FD9eicpJwdgQ==", + "dev": true, + "requires": { + "methods": "^1.1.2", + "superagent": "^6.1.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/package.json b/package.json index 4a0a3ec..c9fa4f1 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "esbuild": "^0.12.0", "esbuild-register": "^2.5.0", "eslint": "^7.26.0", + "supertest": "^6.1.3", "ts-node": "^9.1.1", "typedoc": "^0.20.36", "typescript": "^4.2.4" diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index c9334aa..fd00fc7 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -2,7 +2,7 @@ // typescript is really a fucking joke -import configOptionType from './config-option-type' +import { configOptionType } from './types' import EventEmitter from 'events' class BaseTools extends EventEmitter { @@ -19,6 +19,9 @@ class BaseTools extends EventEmitter { */ protected parsePayload(req: any): Promise { return new Promise((resolver: any, rejecter: any): void => { + // V.2 here we also need to parse the header and add to the json + // and the result object will become { payload: Object, header: Object } + let body: Array = [] req .on('data', (chunk: any) => { diff --git a/src/lib/index.ts b/src/lib/index.ts index 838e28e..39c1380 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -3,6 +3,6 @@ import createServer from './server' -export { createServer } +export { createServer } export { debugFn } from './helpers' export { BaseTools, configOptionType } from './base-tools' diff --git a/src/lib/config-option-type.ts b/src/lib/types.ts similarity index 88% rename from src/lib/config-option-type.ts rename to src/lib/types.ts index 28d48db..b39649f 100644 --- a/src/lib/config-option-type.ts +++ b/src/lib/types.ts @@ -12,4 +12,4 @@ type configOptionType = { inited?: boolean } -export default configOptionType +export { configOptionType } diff --git a/src/provider/gitee/verify.ts b/src/provider/gitee/verify.ts index 0724c99..8e98b2e 100644 --- a/src/provider/gitee/verify.ts +++ b/src/provider/gitee/verify.ts @@ -8,7 +8,7 @@ import { createHmac } from 'crypto' * @param {string} secretKey the secret key set during sestting up the webhook * @param {number} timestamp this timestamp send from the git provider server */ -export function verifyFn(secretKey: string, timestamp: number): string { +export function verifyKeyFn(secretKey: string, timestamp: number): string { const secret_enc = Buffer.from(secret, 'utf8') const string_to_sign = `${timestamp}\n${secret_enc}` const hmac = createHmac('sha256', secret_enc) @@ -16,3 +16,12 @@ export function verifyFn(secretKey: string, timestamp: number): string { return encodeURIComponent(data.digest('base64')) } + +/** + * gitee has it's own payload structure therefore we need to check if we have those things in the header + * @param {*} req request object from http server + * @return {*} + */ +export function verifyHeader(req: any): any { + +} -- Gitee From c46d90c4adb241b2e541a11cb51165ac514a1af1 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 13:35:23 +0800 Subject: [PATCH 02/21] add the top level README file --- README.md | 251 ++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/lib/base-tools.ts | 4 +- src/lib/types.ts | 7 +- tests/gitee.test.ts | 4 +- tests/partial.test.ts | 4 + 6 files changed, 266 insertions(+), 5 deletions(-) create mode 100644 README.md create mode 100644 tests/partial.test.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d330e1 --- /dev/null +++ b/README.md @@ -0,0 +1,251 @@ +# git-webhook-ci [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] +> A Git (github/gitee) webhook callback server to fetch new code (poor man CI) + +This little tool is born out of real projects. Keep having to deploy and setup demo site etc. Why bother if you own the git account? +You just need a new cert from github, add this to your project, and setup accordingly, and Viola, you get your own poor man CI :) + +## Installation + +```sh + $ npm install --save git-webhook-ci +``` + +or + +```sh + $ yarn add git-webhook-ci +``` + +## Configuration and usage + +Create a js file (normally on your project root directory). Let's call it `webhook.js`. + +```js +const gitWebhook = require('git-webhook-ci'); +const config = { + "secret": "your-github-webhook-secret", + "path": "/webhook", + "port": 8081, + "branch": "refs/heads/master", // New in 0.4.1 you can pass * wildcard to listen to all branches + "cmd": "git pull origin master --no-edit" +}; + +gitWebhook(config); +``` + +*Important change, we no longer support github by default. Instead if you don't add the provider view, it will be gitlab!* + + +The minimum setup can be like this: + +```js + + // Default is gitlab + gitWebhook({secret: "your-gitlab-token"}); + + // For Gitee + gitWebhook({secret: 'your-gitee-password', provider: 'gitee'}); + + // For Github + gitWebhook({secret: 'your-github-webhook-secret', provider: 'github'}); + +``` + + +## New in 0.4.0 - cmd accept (String) command to run or (Function) callback + +The `cmd` config option now accept a function. + +The signature as follow + +```js +{ + secret: 'your-secret-between-you-and-gitlab', + cmd: (result, opt, ref) => { + // result has 3 properties + // 1. payload + // 2. host + // 3. event - from github / gitee + // opt is an environment variable that you can pass to the spawn + } +} +``` + +Example how to combine the wildcard branch option, and a function callback + +```js +const gitWebhook = require('git-webook-ci'); +const { spawn } = require('child_process'); + +const server = gitWebhook({ + secret: 'your-secret-between-you-and-github', + branch: '*', + cmd: (result, opt, ref) => { + switch (ref) { + case 'refs/heads/master': + const e1 = spawn('npm', ['run', 'something'], opt); + break; + case 'refs/heads/develop': + const e2 = spawn('npm', ['run', 'something-else'], opt); + break; + default: + // do special stuff using the result object + specialFunc(result.payload, opt); + } + } +}); + +``` + +As you can see from the code example from above. The method `gitWebhook` actually return the +`server` instance from `http.createServer`. So you can make it to stop, restart etc easily. + +## New in 0.4.x - support 码云 Gitee.com + +You can now pass a new configuration option `provider`: +```js +{ + secret: 'your-password-between-you-and-gitee', + provider: 'gitee' +} +``` + +## New in 0.5.x - support Gitlab.com + +You just need to change the provider to `gitlab`: (*This is the default since V.0.9.x*) + +```js +{ + secret: 'your-gitlab-token', + provider: 'gitlab' +} +``` + +## New in 0.8.x - support 微信小程序消息服务 Wechat mini app callback + +We have added a new provider here - it's not a git repo. It supports the [Wechat callback](https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/callback_help.html). + +**There are several different between wechat callback and the other providers** + +There is a new property that you need to supply when you init your webhook with Wechat. +Because this is a two step process. Once your server is verify with Wechat server. +They will just push data over to the url. So you need to run this once like so. + +First you need to run with the `inited:false` (default) + +```js +{ + secret: 'the-token-you-setup-with-wechat', + provider: 'wechat', + inited: false // this is default +} +``` + +Then re-config your webhook to run normal operation: + +```js +{ + secret: 'the-token-you-setup-with-wechat', + provider: 'wechat', + inited: true // default: false +} +``` + +There is a complete example in the *Wiki coming back soon* to demonstrate how you can do this automatically, +with additional module `fs-extra`, `nodemon` and `node-config`. + +--- + +If you are using function as your `cmd` property, there will only be two parameters supply, when execute your callback. + +```js + { + cmd: (result, opt) => { + // there is no ref + } + } +``` + +### Full configuration properties + +| Property name | Description | Default | Type | +| ------------- | ------------- | ---------| -----| +| dir | Where the git root directory is, default to where it gets call | `process.cwd()` | String | +| secret | A secret key pass to encrypt data between github and your server | '' | String | +| path | The path where the web hook call to your server | `/webhook` | String | +| port | The port number where this callback server running on | `8081` | Integer | +| branch | The branch where you will trigger action when received event from github. You can pass `*` wildcard to listen to all the branches | `refs/heads/master` | String | +| cmd | The command to execute when callback happens. You can also pass this as a function (see above for signature) and especially useful when you use `*` for branch | `git pull origin master --no-edit` | String | +| inited | only available with `wechat` provider | `false` | Boolean | + +### Debug option + +Internally we use `debug` to track what's going on. So you can just pass the env during the start up of the script to debug your setup. + +```sh + DEBUG=* node ./webhook.js +``` + +If you do that, you will see a huge amount of info. All our debug flags are prefixed with `git-webhook-ci`, +and here is the list of all the keys we use in this npm. + +- git-webhook-ci:main +- git-webhook-ci:gitlab +- git-webhook-ci:github +- git-webhook-ci:gitee +- git-webhook-ci:wechat +- git-webhook-ci:demo (only in test) +- git-webhook-ci:test + +For example: + +```sh + DEBUG=git-webhook-ci:main,git-webhook-ci:wechat node ./webhook.js +``` + +Then you will only see the main (top interface) and the Wechat internal debug messages. + +## CLI + +You can install this tools globally. + +```sh + $ npm install git-webhook-ci --global +``` + +Then you can call it from command line like so + +```sh + $ git-webhook-ci /path/to/your/git --secret secret-you-setup + +``` + +Or in your `package.json` + +```js + { + "scripts": { + "webhook": "git-webhook-ci ./ --secret secret-you-setup" + } + } +``` + +Then just run it with `npm run webhook` + +## HOW TO + +Check our *Wiki will be back shortly* for more information about how to setup your app. + +## License + +MIT © [NEWBRAN.CH](newbran.ch) + + +[npm-image]: https://badge.fury.io/js/git-webhook-ci.svg +[npm-url]: https://npmjs.org/package/git-webhook-ci +[travis-image]: https://travis-ci.org/NewbranLTD/git-webhook-ci.svg?branch=master +[travis-url]: https://travis-ci.org/NewbranLTD/git-webhook-ci +[daviddm-image]: https://david-dm.org/NewbranLTD/git-webhook-ci.svg?theme=shields.io +[daviddm-url]: https://david-dm.org/NewbranLTD/git-webhook-ci + +Power by [generator-nodex](https://gitlab.com/newbranltd/generator-nodex). diff --git a/package.json b/package.json index c9fa4f1..d2e2516 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "dist/index.js", "scripts": { "test": "ava", + "test:p": "ava --verbose ./tests/partial.test.ts", "lint": "eslint src/ --ext .js,.jsx,.ts,.tsx", "init": "tsc --init", "build": "tsc -p tsconfig.json", diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index fd00fc7..770baac 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -2,7 +2,7 @@ // typescript is really a fucking joke -import { configOptionType } from './types' +import { configOptionType, resolvedPayloadType } from './types' import EventEmitter from 'events' class BaseTools extends EventEmitter { @@ -17,7 +17,7 @@ class BaseTools extends EventEmitter { * @param {object} req the request Object * @return {object} Promise */ - protected parsePayload(req: any): Promise { + protected parsePayload(req: any): Promise { return new Promise((resolver: any, rejecter: any): void => { // V.2 here we also need to parse the header and add to the json // and the result object will become { payload: Object, header: Object } diff --git a/src/lib/types.ts b/src/lib/types.ts index b39649f..df6e951 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -12,4 +12,9 @@ type configOptionType = { inited?: boolean } -export { configOptionType } +type resolvedPayloadType = { + header: any, + payload: any +} + +export { configOptionType, resolvedPayloadType } diff --git a/tests/gitee.test.ts b/tests/gitee.test.ts index f88cbd3..27a4fa4 100644 --- a/tests/gitee.test.ts +++ b/tests/gitee.test.ts @@ -3,7 +3,7 @@ import test from 'ava' import { gitWebhookCi } from '../src/main' - +import request from 'supertest' import { SECRET_KEY } from './fixtures/secret' import { getFakeCallback } from './fixtures/fake-callback' @@ -14,7 +14,7 @@ test.cb(`Should able to use a gitee config to connect`, t => { gitWebhookCi({ secret: SECRET_KEY, cmd: () => { - + } }) diff --git a/tests/partial.test.ts b/tests/partial.test.ts new file mode 100644 index 0000000..194057e --- /dev/null +++ b/tests/partial.test.ts @@ -0,0 +1,4 @@ +// tests/partial.test.ts + +// using this to do TBD-ish to develop the new features +// and testing the code part by part -- Gitee From 10a12689c6079e9444d4f5d7a4c8a8cb3fdd883b Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 14:02:04 +0800 Subject: [PATCH 03/21] completely change the createBareServer signature --- src/lib/server.ts | 25 +++++++++++++++++++++---- tests/partial.test.ts | 13 +++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/lib/server.ts b/src/lib/server.ts index 7e35ed1..3d408d6 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -2,11 +2,28 @@ // create a barebone server import { createServer } from 'http' -function createBareServer(config: any, callback: any, debug: any) { - return createServer(callback) - .listen(config.port, (): void => { +/** + * super simple http server using build-in node http + * @param {function} callback + * @param {object} config + * @param {function} debug + * @return {http.Server} + */ +function createBareServer(callback: any, config: any = {}, debug: any = () => {}): any { + const srv = createServer(callback) + if (process.env.NODE_ENV === 'test') { + + return srv + } + if (!config.port) { + throw new Error(`Expect a config.port property!`) + } + + return srv.listen(config.port, (): void => { + try { debug(`${config.provider} webhook server start @ ${config.port}`) - }) + } catch(e) {} + }) } export default createBareServer diff --git a/tests/partial.test.ts b/tests/partial.test.ts index 194057e..60eefa4 100644 --- a/tests/partial.test.ts +++ b/tests/partial.test.ts @@ -1,4 +1,13 @@ // tests/partial.test.ts - +import test from 'ava' +import request from 'supertest' // using this to do TBD-ish to develop the new features -// and testing the code part by part +// and testing the code part by part + +import { createServer } from 'http' + +test(`Try out the supertest with fake headers`, t => { + request() + + +}) -- Gitee From 04d67f3a345bc7d55bae0e90dbd297aeb01998b4 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 14:41:20 +0800 Subject: [PATCH 04/21] update the base class to have the header as well as payload --- src/lib/base-tools.ts | 31 ++++++++++++++++++++--- src/provider/gitee/gitee-handler.ts | 2 +- tests/fixtures/gitee.ts | 2 +- tests/fixtures/github.ts | 2 +- tests/partial.test.ts | 39 ++++++++++++++++++++++++++--- 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index 770baac..7d20150 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -12,6 +12,22 @@ class BaseTools extends EventEmitter { super() } + /** + * parsing the raw heading and keep them in original format (no lower case) + * @param {*} req request object + * @return {object} + */ + protected parseHeader(req): any { + const headers = req.rawHeaders + const ctn = headers.length + const h = {} + for (let i = 0; i < ctn; i += 2) { + h[ headers[i] ] = headers[ i + 1] + } + + return h + } + /** * Extract the json payload * @param {object} req the request Object @@ -21,15 +37,24 @@ class BaseTools extends EventEmitter { return new Promise((resolver: any, rejecter: any): void => { // V.2 here we also need to parse the header and add to the json // and the result object will become { payload: Object, header: Object } - + const header = this.parseHeader(req) let body: Array = [] + req .on('data', (chunk: any) => { body.push(chunk) }) .on('end', () => { - const json: string = Buffer.concat(body).toString() - resolver(JSON.parse(json)) + // should catch error here as well + try { + const json: string = Buffer.concat(body).toString() + resolver({ + header, + payload: JSON.parse(json) + }) + } catch(e) { + rejecter(e) + } }) .on('error', rejecter) // just throw the rejecter in to handle it }) diff --git a/src/provider/gitee/gitee-handler.ts b/src/provider/gitee/gitee-handler.ts index fea4a04..652e982 100644 --- a/src/provider/gitee/gitee-handler.ts +++ b/src/provider/gitee/gitee-handler.ts @@ -16,7 +16,7 @@ export class GiteeHandler extends BaseTools { * @return {null} nothing */ public handler(req: any, res: any, callback: any): any { - if (req.url.split('?').shift() !== this.options.path || req.method !== 'POST') { + if (req.method !== 'POST' || req.url.split('?').shift() !== this.options.path) { return callback() // This is the only time we use the callback } this.parsePayload(req) diff --git a/tests/fixtures/gitee.ts b/tests/fixtures/gitee.ts index 49f2a40..4ac90fd 100644 --- a/tests/fixtures/gitee.ts +++ b/tests/fixtures/gitee.ts @@ -2,7 +2,7 @@ // the Gitee webhook headers and payload -export const headers = { +export const header = { 'Content-Type': 'application/json', // # 默认为 application/json , 若是旧版钩子(已不维护)为 application/x-www-form-urlencoded 'User-Agent': 'git-oschina-hook',   // # 固定为 git-oschina-hook,可用于标识为来自 gitee 的请 'X-Gitee-Token': 'webhook password/sign', // # 用户新建 WebHook 时提供的密码或根据提供的签名密钥计算后的签名 diff --git a/tests/fixtures/github.ts b/tests/fixtures/github.ts index af517bd..52f4e31 100644 --- a/tests/fixtures/github.ts +++ b/tests/fixtures/github.ts @@ -1,6 +1,6 @@ // tests/fixtures/github.ts -export const headers = { +export const header = { } diff --git a/tests/partial.test.ts b/tests/partial.test.ts index 60eefa4..20a73d5 100644 --- a/tests/partial.test.ts +++ b/tests/partial.test.ts @@ -4,10 +4,43 @@ import request from 'supertest' // using this to do TBD-ish to develop the new features // and testing the code part by part -import { createServer } from 'http' +import createServer from '../src/lib/server' +import { header, payload } from './fixtures/gitee.ts' -test(`Try out the supertest with fake headers`, t => { - request() +// @NOTE should sub class from the base-tools but this will do for now +function parseHeader(req): any { + const headers = req.rawHeaders + const ctn = headers.length + const h = {} + for (let i = 0; i < ctn; i += 2) { + h[ headers[i] ] = headers[ i + 1] + } + return h +} +// simple callback handler +function cb(req: any, res: any): void { + console.log(parseHeader(req)) + res.writeHead(200, { 'content-type': 'application/json' }) + res.end('{"ok":true}') +} + +// just change the timestamp field +function getHeader(): any { + return Object.assign({}, header, {'X-Gitee-Timestamp': Date.now()}) +} + + +test.cb(`Try out the supertest with fake headers`, t => { + t.plan(1) + + request(createServer(cb)) + .post('/') + .set(getHeader()) + .send(payload) + .expect(200, () => { + t.pass() + t.end() + }) }) -- Gitee From fce2c4ba0a831f0dfdf60fcf529ff23b6c3a4558 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 15:21:22 +0800 Subject: [PATCH 05/21] finish the gitee verify handler --- src/provider/gitee/gitee-handler.ts | 15 ++++++++------- src/provider/gitee/verify.ts | 27 ++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/provider/gitee/gitee-handler.ts b/src/provider/gitee/gitee-handler.ts index 652e982..7e81059 100644 --- a/src/provider/gitee/gitee-handler.ts +++ b/src/provider/gitee/gitee-handler.ts @@ -1,6 +1,7 @@ // src/provider/gitee/gitee-class.ts import { BaseTools, configOptionType } from '../../lib/base-tools' +import { verifyHandler } from './verify' export class GiteeHandler extends BaseTools { @@ -20,8 +21,8 @@ export class GiteeHandler extends BaseTools { return callback() // This is the only time we use the callback } this.parsePayload(req) - .then(payload => { - this.verify(payload) + .then(obj => { + this.verify(obj) .then(result => { this.resSuccess(res, req, result) }) @@ -36,13 +37,13 @@ export class GiteeHandler extends BaseTools { * @param {object} payload Content * @return {object} promise */ - private verify(payload: any): Promise { + private verify(obj: any): Promise { return new Promise((resolver: any, rejecter: any): void => { - // Log('parsed payload', payload); - if (payload.password === this.options.secret) { + const { header, payload } = obj + if (verifyHandler(header, this.options.secret)) { resolver(payload) } else { - rejecter(new Error('Verify failed')) + rejecter(new Error('Gitee verify failed')) } }) } @@ -56,7 +57,7 @@ export class GiteeHandler extends BaseTools { res.writeHead(200, { 'content-type': 'application/json' }) res.end('{"ok":true}') // Check the result if this is what we wanted - if (result.hook_name === 'push_hooks') { + if (result.hook_name === 'push_hooks') { // @TODO check if this is still correct this.emit('push', { payload: result, host: req.headers.host, diff --git a/src/provider/gitee/verify.ts b/src/provider/gitee/verify.ts index 8e98b2e..a8ce3dd 100644 --- a/src/provider/gitee/verify.ts +++ b/src/provider/gitee/verify.ts @@ -1,14 +1,16 @@ // src/provider/gitee/secret.ts // see here: https://gitee.com/help/articles/4290#article-header3 - +import { debugFn } from '../../lib/helpers' import { createHmac } from 'crypto' +// use the debug to find out what went wrong +const debug = debugFn('git-webhook-ci:gitee:verify') /** * create the secret key to compare * @param {string} secretKey the secret key set during sestting up the webhook * @param {number} timestamp this timestamp send from the git provider server */ -export function verifyKeyFn(secretKey: string, timestamp: number): string { +function verifyKeyFn(secretKey: string, timestamp: number): string { const secret_enc = Buffer.from(secret, 'utf8') const string_to_sign = `${timestamp}\n${secret_enc}` const hmac = createHmac('sha256', secret_enc) @@ -19,9 +21,24 @@ export function verifyKeyFn(secretKey: string, timestamp: number): string { /** * gitee has it's own payload structure therefore we need to check if we have those things in the header - * @param {*} req request object from http server - * @return {*} + * @param {*} header the parsed header from req + * @param {string} secretKey the secret key provided when setup the webhook + * @return {boolean} */ -export function verifyHeader(req: any): any { +export function verifyHandler(header: any, secretKey: string): boolean { + if (header['User-Agent'] === 'git-oschina-hook') { + debug('User-Agent passed', header['User-Agent']) + const expected = ['X-Gitee-Token', 'X-Gitee-Timestamp', 'X-Gitee-Event'].filter(key => header[key] !== undefined).length + if (expected === 3) { + debug('Expected header passed') + if (header['X-Gitee-Token'] === verifyKeyFn(secretKey, parseInt(header['X-Gitee-Timestamp'])) { + + return true + } else { + debug('verify the x-gitee-token failed') + } + } + } + return false } -- Gitee From c603e7b8a05b34360690b09716302787ee2bc1cc Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 15:29:37 +0800 Subject: [PATCH 06/21] update the gitee instance to match the new handler signatures --- src/provider/gitee/index.ts | 2 +- src/provider/gitee/verify.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/provider/gitee/index.ts b/src/provider/gitee/index.ts index 33d4941..4c6a1b5 100644 --- a/src/provider/gitee/index.ts +++ b/src/provider/gitee/index.ts @@ -36,7 +36,6 @@ function createGiteeServer(config: configOptionType, opt: any, callback: any, er // return the server instance return createServer( - config, (req: any, res: any) => { gitee.handler(req, res, (err: any) => { debug('The url got called! [%s]', req.url, err) @@ -46,6 +45,7 @@ function createGiteeServer(config: configOptionType, opt: any, callback: any, er res.end('-- no such location --') }) }, + config, debug ) } diff --git a/src/provider/gitee/verify.ts b/src/provider/gitee/verify.ts index a8ce3dd..64ddebb 100644 --- a/src/provider/gitee/verify.ts +++ b/src/provider/gitee/verify.ts @@ -1,8 +1,8 @@ // src/provider/gitee/secret.ts // see here: https://gitee.com/help/articles/4290#article-header3 -import { debugFn } from '../../lib/helpers' import { createHmac } from 'crypto' // use the debug to find out what went wrong +import { debugFn } from '../../lib/helpers' const debug = debugFn('git-webhook-ci:gitee:verify') /** @@ -10,7 +10,7 @@ const debug = debugFn('git-webhook-ci:gitee:verify') * @param {string} secretKey the secret key set during sestting up the webhook * @param {number} timestamp this timestamp send from the git provider server */ -function verifyKeyFn(secretKey: string, timestamp: number): string { +export function verifyKeyFn(secretKey: string, timestamp: number): string { const secret_enc = Buffer.from(secret, 'utf8') const string_to_sign = `${timestamp}\n${secret_enc}` const hmac = createHmac('sha256', secret_enc) -- Gitee From 1cf3b1a116444368509345ff85a93465df5451bf Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 15:32:38 +0800 Subject: [PATCH 07/21] update the others to match the new handler signatures --- src/provider/github/index.ts | 2 +- src/provider/gitlab/index.ts | 2 +- src/provider/wechat/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/provider/github/index.ts b/src/provider/github/index.ts index ba8889c..7355fac 100644 --- a/src/provider/github/index.ts +++ b/src/provider/github/index.ts @@ -31,7 +31,6 @@ function createGithubServer(config: configOptionType, opt: any, callback: any, e }) return createServer( - config, (req: any, res: any): void => { handler(req, res, (err: any): void => { @@ -42,6 +41,7 @@ function createGithubServer(config: configOptionType, opt: any, callback: any, e res.end('-- no such location --') }) }, + config, debug ) } diff --git a/src/provider/gitlab/index.ts b/src/provider/gitlab/index.ts index 06b6a9c..dde557c 100644 --- a/src/provider/gitlab/index.ts +++ b/src/provider/gitlab/index.ts @@ -27,7 +27,6 @@ function createGitlabServer(config: configOptionType, opt: any, callback: any, e // return the http server return createServer( - config, (req: any, res: any): void => { gitlab.handler(req, res, (err: any) => { @@ -38,6 +37,7 @@ function createGitlabServer(config: configOptionType, opt: any, callback: any, e res.end('-- no such location --') }) }, + config, debug ) } diff --git a/src/provider/wechat/index.ts b/src/provider/wechat/index.ts index eb6f310..2569071 100644 --- a/src/provider/wechat/index.ts +++ b/src/provider/wechat/index.ts @@ -20,7 +20,6 @@ function createWechatServer(config: configOptionType, opt: any, callback: any, e }) return createServer( - config, (req: any, res: any): void => { wechat.handler(req, res, (err:any): void => { errorHandler(err) @@ -30,6 +29,7 @@ function createWechatServer(config: configOptionType, opt: any, callback: any, e res.end('-- no such location --') }) }, + config, debug ) } -- Gitee From f3ca8e9052f448a66bf673fde782183b9e528492 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 20:55:27 +0800 Subject: [PATCH 08/21] finish all the refactoring start testing the gitee provider with supertest --- package.json | 1 + src/lib/base-tools.ts | 5 ++--- src/main.ts | 2 +- src/provider/gitee/verify.ts | 6 +++--- tests/fixtures/fake-callback.ts | 23 +++++++++++++++++++---- tests/gitee.test.ts | 30 +++++++++++++++++++++--------- 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index d2e2516..a677bb6 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "test": "ava", "test:p": "ava --verbose ./tests/partial.test.ts", + "test:gitee": "DEBUG=git-webhook-ci:* ava --verbose ./tests/gitee.test.ts", "lint": "eslint src/ --ext .js,.jsx,.ts,.tsx", "init": "tsc --init", "build": "tsc -p tsconfig.json", diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index 7d20150..ae9736b 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -1,9 +1,8 @@ // src/lib/base-tools -// typescript is really a fucking joke - -import { configOptionType, resolvedPayloadType } from './types' import EventEmitter from 'events' +import { configOptionType, resolvedPayloadType } from './types' + class BaseTools extends EventEmitter { diff --git a/src/main.ts b/src/main.ts index 98ffe71..24f8b73 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,7 @@ import { spawn } from 'child_process' import { getProvider } from './provider' -import defaultOptions from './lib/option' +import { defaultOptions } from './lib/option' import { debugFn } from './lib' const debug = debugFn('git-webhook-ci:main') diff --git a/src/provider/gitee/verify.ts b/src/provider/gitee/verify.ts index 64ddebb..32fd090 100644 --- a/src/provider/gitee/verify.ts +++ b/src/provider/gitee/verify.ts @@ -10,8 +10,8 @@ const debug = debugFn('git-webhook-ci:gitee:verify') * @param {string} secretKey the secret key set during sestting up the webhook * @param {number} timestamp this timestamp send from the git provider server */ -export function verifyKeyFn(secretKey: string, timestamp: number): string { - const secret_enc = Buffer.from(secret, 'utf8') +export function getToken(secretKey: string, timestamp: number): string { + const secret_enc = Buffer.from(secretKey, 'utf8') const string_to_sign = `${timestamp}\n${secret_enc}` const hmac = createHmac('sha256', secret_enc) const data = hmac.update(string_to_sign) @@ -31,7 +31,7 @@ export function verifyHandler(header: any, secretKey: string): boolean { const expected = ['X-Gitee-Token', 'X-Gitee-Timestamp', 'X-Gitee-Event'].filter(key => header[key] !== undefined).length if (expected === 3) { debug('Expected header passed') - if (header['X-Gitee-Token'] === verifyKeyFn(secretKey, parseInt(header['X-Gitee-Timestamp'])) { + if (header['X-Gitee-Token'] === getToken(secretKey, parseInt(header['X-Gitee-Timestamp']))) { return true } else { diff --git a/tests/fixtures/fake-callback.ts b/tests/fixtures/fake-callback.ts index d351126..26f92ec 100644 --- a/tests/fixtures/fake-callback.ts +++ b/tests/fixtures/fake-callback.ts @@ -1,9 +1,24 @@ // tests/fixtures/fake-callback.ts +import { getToken as giteeGetToken } from '../../src/provider/gitee/verify' +import { header as giteeHeader, payload as giteePayload } from './gitee' +import { SECRET_KEY } from './secret' -import { headers as giteeHeaders, payload as giteePayload } from './gitee' +// this will create a fake callback to the webhook listener with specific payload +export function getFakeData(provider: string): any { + switch(provider) { + case 'gitee': + const ts = Date.now() + const token = giteeGetToken(SECRET_KEY, ts) + return { + header: Object.assign({}, giteeHeader, { + 'X-Gitee-Token': token, + 'X-Gitee-Timestamp': ts + }), + payload: giteePayload + } -// this will create a fake callback to the webhook listener with specific payload -export function getFakeCallback(provider: string, port: number): any { - + default: + throw new Error(`Unknown provider ${provider}`) + } } diff --git a/tests/gitee.test.ts b/tests/gitee.test.ts index 27a4fa4..05040bc 100644 --- a/tests/gitee.test.ts +++ b/tests/gitee.test.ts @@ -1,21 +1,33 @@ // tests/gitee.test.ts import test from 'ava' +import request from 'supertest' import { gitWebhookCi } from '../src/main' -import request from 'supertest' import { SECRET_KEY } from './fixtures/secret' -import { getFakeCallback } from './fixtures/fake-callback' +import { getFakeData } from './fixtures/fake-callback' + + +test.cb(`Should able to use a gitee config to listen to the webhook event`, t => { + t.plan(1) + + const { header, payload } = getFakeData('gitee') + request( + gitWebhookCi({ + secret: SECRET_KEY, + cmd: (...args) => { -test.cb(`Should able to use a gitee config to connect`, t => { - const fn = getFakeCallback('gitee') + console.log(args) - gitWebhookCi({ - secret: SECRET_KEY, - cmd: () => { + t.pass() + t.end() + } + }) + ) + .post('/webhook') + .set(header) + .send(payload) - } - }) }) -- Gitee From b614202c2974931e3a6da777eeff387050713f42 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 21:05:28 +0800 Subject: [PATCH 09/21] gitee test and passed --- src/provider/gitee/gitee-handler.ts | 6 ++++++ src/provider/gitee/index.ts | 7 +++++-- tests/fixtures/gitee.ts | 2 +- tests/gitee.test.ts | 4 +++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/provider/gitee/gitee-handler.ts b/src/provider/gitee/gitee-handler.ts index 7e81059..44c380b 100644 --- a/src/provider/gitee/gitee-handler.ts +++ b/src/provider/gitee/gitee-handler.ts @@ -2,6 +2,9 @@ import { BaseTools, configOptionType } from '../../lib/base-tools' import { verifyHandler } from './verify' +import { debugFn } from '../../lib/helpers' + +const debug = debugFn('git-webhook-ci:gitee:handler') export class GiteeHandler extends BaseTools { @@ -17,7 +20,10 @@ export class GiteeHandler extends BaseTools { * @return {null} nothing */ public handler(req: any, res: any, callback: any): any { + debug(`got call here`) if (req.method !== 'POST' || req.url.split('?').shift() !== this.options.path) { + debug(req.url) + debug(this.options.path) return callback() // This is the only time we use the callback } this.parsePayload(req) diff --git a/src/provider/gitee/index.ts b/src/provider/gitee/index.ts index 4c6a1b5..5ef30e9 100644 --- a/src/provider/gitee/index.ts +++ b/src/provider/gitee/index.ts @@ -9,8 +9,8 @@ const debug = debugFn('git-webhook-ci:gitee') /** * The main method to handle the server create and run the whole service for gitee - * @param {configOptionType} config - * @param {object} opt + * @param {configOptionType} config for the overall setup of the system + * @param {object} opt this provide the environment variables to the cmd to execute later * @param {function} callback * @param {function} errorHandler optional * @return {http server instance} @@ -37,6 +37,9 @@ function createGiteeServer(config: configOptionType, opt: any, callback: any, er // return the server instance return createServer( (req: any, res: any) => { + + debug(`server callback executed`) + gitee.handler(req, res, (err: any) => { debug('The url got called! [%s]', req.url, err) errorHandler(req.url, err) diff --git a/tests/fixtures/gitee.ts b/tests/fixtures/gitee.ts index 4ac90fd..a6f719d 100644 --- a/tests/fixtures/gitee.ts +++ b/tests/fixtures/gitee.ts @@ -17,7 +17,7 @@ export const payload = { "hook_url": "http://gitee.com/liwen/gitos/hooks/1/edit", "timestamp": "1576754827988", "sign": "rLEHLuZRIQHuTPeXMib9Czoq9dVXO4TsQcmQQHtjXHA=", - "ref": "refs/heads/change_commitlint_config", + "ref": "refs/heads/master", "before": "0000000000000000000000000000000000000000", "after": "1cdcd819599cbb4099289dbbec762452f006cb40", "created": true, diff --git a/tests/gitee.test.ts b/tests/gitee.test.ts index 05040bc..01141ed 100644 --- a/tests/gitee.test.ts +++ b/tests/gitee.test.ts @@ -28,6 +28,8 @@ test.cb(`Should able to use a gitee config to listen to the webhook event`, t => .post('/webhook') .set(header) .send(payload) - + .expect(200, () => { // we must call here to let supertest to exeucte the call + // console.log(`200 back`) + }) }) -- Gitee From fa825a16696f83e5dc3b4c0e09a374cb8aa49029 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 21:11:45 +0800 Subject: [PATCH 10/21] able to build but need to fix those typescript idiocrocy --- package-lock.json | 481 ---------------------------------------------- package.json | 5 +- ts-run.js | 3 + 3 files changed, 4 insertions(+), 485 deletions(-) create mode 100644 ts-run.js diff --git a/package-lock.json b/package-lock.json index 122b443..bc1aa74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,8 +29,6 @@ "esbuild-register": "^2.5.0", "eslint": "^7.26.0", "supertest": "^6.1.3", - "ts-node": "^9.1.1", - "typedoc": "^0.20.36", "typescript": "^4.2.4" } }, @@ -652,12 +650,6 @@ "node": ">= 8" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -717,15 +709,6 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/ava": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/ava/-/ava-3.15.0.tgz", @@ -1298,15 +1281,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1430,12 +1404,6 @@ "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", "dev": true }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1624,15 +1592,6 @@ "node": ">=0.4.0" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2441,27 +2400,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -2645,15 +2583,6 @@ "node": ">=10" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/irregular-plurals": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", @@ -3047,21 +2976,6 @@ "node": ">=0.10.0" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3086,12 +3000,6 @@ "semver": "bin/semver.js" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -3115,18 +3023,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/marked": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz", - "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==", - "dev": true, - "bin": { - "marked": "bin/marked" - }, - "engines": { - "node": ">= 8.16.2" - } - }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -3403,12 +3299,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -3481,15 +3371,6 @@ "node": ">=6" } }, - "node_modules/onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "dependencies": { - "lru-cache": "^5.1.1" - } - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -4243,18 +4124,6 @@ "node": ">=8.10.0" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/redent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", @@ -4531,33 +4400,6 @@ "node": ">=8" } }, - "node_modules/shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shiki": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.3.tgz", - "integrity": "sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA==", - "dev": true, - "dependencies": { - "onigasm": "^2.2.5", - "vscode-textmate": "^5.2.0" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -5053,32 +4895,6 @@ "node": ">=0.10.0" } }, - "node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "typescript": ">=2.7" - } - }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -5132,58 +4948,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typedoc": { - "version": "0.20.36", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.20.36.tgz", - "integrity": "sha512-qFU+DWMV/hifQ9ZAlTjdFO9wbUIHuUBpNXzv68ZyURAP9pInjZiO4+jCPeAzHVcaBCHER9WL/+YzzTt6ZlN/Nw==", - "dev": true, - "dependencies": { - "colors": "^1.4.0", - "fs-extra": "^9.1.0", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", - "lunr": "^2.3.9", - "marked": "^2.0.3", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shelljs": "^0.8.4", - "shiki": "^0.9.3", - "typedoc-default-themes": "^0.12.10" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 10.8.0" - }, - "peerDependencies": { - "typescript": "3.9.x || 4.0.x || 4.1.x || 4.2.x" - } - }, - "node_modules/typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/typedoc/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/typescript": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", @@ -5197,19 +4961,6 @@ "node": ">=4.2.0" } }, - "node_modules/uglify-js": { - "version": "3.13.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.9.tgz", - "integrity": "sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -5332,12 +5083,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/vscode-textmate": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.4.0.tgz", - "integrity": "sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w==", - "dev": true - }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -5392,12 +5137,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -5479,12 +5218,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -5510,15 +5243,6 @@ "node": ">=10" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -5975,12 +5699,6 @@ "picomatch": "^2.0.4" } }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -6025,12 +5743,6 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, "ava": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/ava/-/ava-3.15.0.tgz", @@ -6458,12 +6170,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6568,12 +6274,6 @@ "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", "dev": true }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6713,12 +6413,6 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7333,19 +7027,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -7464,12 +7145,6 @@ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, "irregular-plurals": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", @@ -7771,21 +7446,6 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -7803,12 +7463,6 @@ } } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -7823,12 +7477,6 @@ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==" }, - "marked": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz", - "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==", - "dev": true - }, "matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", @@ -8028,12 +7676,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -8090,15 +7732,6 @@ } } }, - "onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "requires": { - "lru-cache": "^5.1.1" - } - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -8630,15 +8263,6 @@ "picomatch": "^2.2.1" } }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, "redent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", @@ -8828,27 +8452,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "shiki": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.3.tgz", - "integrity": "sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA==", - "dev": true, - "requires": { - "onigasm": "^2.2.5", - "vscode-textmate": "^5.2.0" - } - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -9225,20 +8828,6 @@ "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, - "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - } - }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -9277,58 +8866,12 @@ "is-typedarray": "^1.0.0" } }, - "typedoc": { - "version": "0.20.36", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.20.36.tgz", - "integrity": "sha512-qFU+DWMV/hifQ9ZAlTjdFO9wbUIHuUBpNXzv68ZyURAP9pInjZiO4+jCPeAzHVcaBCHER9WL/+YzzTt6ZlN/Nw==", - "dev": true, - "requires": { - "colors": "^1.4.0", - "fs-extra": "^9.1.0", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", - "lunr": "^2.3.9", - "marked": "^2.0.3", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shelljs": "^0.8.4", - "shiki": "^0.9.3", - "typedoc-default-themes": "^0.12.10" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - } - } - }, - "typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true - }, "typescript": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", "dev": true }, - "uglify-js": { - "version": "3.13.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.9.tgz", - "integrity": "sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g==", - "dev": true, - "optional": true - }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -9429,12 +8972,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "vscode-textmate": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.4.0.tgz", - "integrity": "sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w==", - "dev": true - }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -9474,12 +9011,6 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -9542,12 +9073,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -9567,12 +9092,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==" }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index a677bb6..43e4c03 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,7 @@ "init": "tsc --init", "build": "tsc -p tsconfig.json", "clean": "node ./clean.js", - "ts-node": "ts-node", - "docs": "typedoc --entryPoints src/index.ts" + "ts-node": "ts-node" }, "keywords": [ "git", @@ -43,8 +42,6 @@ "esbuild-register": "^2.5.0", "eslint": "^7.26.0", "supertest": "^6.1.3", - "ts-node": "^9.1.1", - "typedoc": "^0.20.36", "typescript": "^4.2.4" }, "ava": { diff --git a/ts-run.js b/ts-run.js new file mode 100644 index 0000000..6c81b79 --- /dev/null +++ b/ts-run.js @@ -0,0 +1,3 @@ +// ts-run.js try to use esbuild-register to run this without the compilation + +// @TODO -- Gitee From 16bfc4312ab510e3e6acc18c895111c7f5d64686 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Fri, 18 Jun 2021 23:00:45 +0800 Subject: [PATCH 11/21] Fix all the moronic error from typescript --- dist/cli.d.ts | 2 + dist/cli.js | 79 ++++++++++++++++++++++ dist/lib/base-tools.d.ts | 26 +++++++ dist/lib/base-tools.js | 67 ++++++++++++++++++ dist/lib/helpers.d.ts | 4 ++ dist/lib/helpers.js | 8 +++ dist/lib/index.d.ts | 4 ++ dist/lib/index.js | 6 ++ dist/lib/option.d.ts | 1 + dist/lib/option.js | 10 +++ dist/lib/server.d.ts | 9 +++ dist/lib/server.js | 26 +++++++ dist/lib/types.d.ts | 15 +++++ dist/lib/types.js | 3 + dist/main.d.ts | 6 ++ dist/main.js | 53 +++++++++++++++ dist/provider/gitee/gitee-handler.d.ts | 24 +++++++ dist/provider/gitee/gitee-handler.js | 74 ++++++++++++++++++++ dist/provider/gitee/index.d.ts | 11 +++ dist/provider/gitee/index.js | 42 ++++++++++++ dist/provider/gitee/verify.d.ts | 13 ++++ dist/provider/gitee/verify.js | 40 +++++++++++ dist/provider/github/index.d.ts | 3 + dist/provider/github/index.js | 37 ++++++++++ dist/provider/gitlab/gitlab-handler.d.ts | 26 +++++++ dist/provider/gitlab/gitlab-handler.js | 73 ++++++++++++++++++++ dist/provider/gitlab/index.d.ts | 3 + dist/provider/gitlab/index.js | 33 +++++++++ dist/provider/index.d.ts | 7 ++ dist/provider/index.js | 25 +++++++ dist/provider/wechat/index.d.ts | 3 + dist/provider/wechat/index.js | 24 +++++++ dist/provider/wechat/wechat-handler.d.ts | 22 ++++++ dist/provider/wechat/wechat-handler.js | 86 ++++++++++++++++++++++++ src/lib/base-tools.ts | 8 +-- src/lib/option.ts | 6 +- src/lib/types.ts | 2 +- src/main.ts | 9 ++- 38 files changed, 879 insertions(+), 11 deletions(-) create mode 100644 dist/cli.d.ts create mode 100644 dist/cli.js create mode 100644 dist/lib/base-tools.d.ts create mode 100644 dist/lib/base-tools.js create mode 100644 dist/lib/helpers.d.ts create mode 100644 dist/lib/helpers.js create mode 100644 dist/lib/index.d.ts create mode 100644 dist/lib/index.js create mode 100644 dist/lib/option.d.ts create mode 100644 dist/lib/option.js create mode 100644 dist/lib/server.d.ts create mode 100644 dist/lib/server.js create mode 100644 dist/lib/types.d.ts create mode 100644 dist/lib/types.js create mode 100644 dist/main.d.ts create mode 100644 dist/main.js create mode 100644 dist/provider/gitee/gitee-handler.d.ts create mode 100644 dist/provider/gitee/gitee-handler.js create mode 100644 dist/provider/gitee/index.d.ts create mode 100644 dist/provider/gitee/index.js create mode 100644 dist/provider/gitee/verify.d.ts create mode 100644 dist/provider/gitee/verify.js create mode 100644 dist/provider/github/index.d.ts create mode 100644 dist/provider/github/index.js create mode 100644 dist/provider/gitlab/gitlab-handler.d.ts create mode 100644 dist/provider/gitlab/gitlab-handler.js create mode 100644 dist/provider/gitlab/index.d.ts create mode 100644 dist/provider/gitlab/index.js create mode 100644 dist/provider/index.d.ts create mode 100644 dist/provider/index.js create mode 100644 dist/provider/wechat/index.d.ts create mode 100644 dist/provider/wechat/index.js create mode 100644 dist/provider/wechat/wechat-handler.d.ts create mode 100644 dist/provider/wechat/wechat-handler.js diff --git a/dist/cli.d.ts b/dist/cli.d.ts new file mode 100644 index 0000000..b798801 --- /dev/null +++ b/dist/cli.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/env node +export {}; diff --git a/dist/cli.js b/dist/cli.js new file mode 100644 index 0000000..0174bf9 --- /dev/null +++ b/dist/cli.js @@ -0,0 +1,79 @@ +#!/usr/bin/env node +/** + * Run from cli + * dir: '', + * path: '/webhook', + * port: 8081, + * branch: 'refs/heads/master', + * cmd: 'git pull origin master --no-edit' + */ +// import { configOptionType } from './lib' +import { gitWebhookCi } from './main'; +import meow from 'meow'; +const helpText = ` + Usage + $ node git-webhook-ci + + $ node git-webhook-ci --secret secret-from-github + + $ node git-webhook-ci --secret secret-from-github --cmd 'git pull origin develop' + + For wechat + + $ node git-webhook-ci -secret wechat-token --inited true --cmd 'some cmd' +`; +const flags = { + port: { + type: 'number', + alias: 'po', + default: 8081 + }, + provider: { + type: 'string', + alias: 'pr', + default: 'github' + }, + dir: { + type: 'string', + alias: 'd' + }, + path: { + type: 'string', + alias: 'p', + default: '/webhook' + }, + branch: { + type: 'string', + alias: 'b', + default: 'refs/heads/master' + }, + secret: { + type: 'string', + alias: 's', + default: '' + }, + cmd: { + type: 'string', + alias: 'c', + default: 'git pull origin master --no-edit' + }, + inited: { + type: 'boolean', + alias: 'i', + default: false + } +}; +const cli = meow(helpText, { + flags, + importMeta: import.meta // TS was complainting about this ... +}); +// Wrap into a method to call +const serve = function (p, flags) { + if (!p || p === '') { + throw new Error('You must provide the ! Check usage for more detail.'); + } + const config = Object.assign({ __caller__: 'meow' }, { dir: p }, flags); + return gitWebhookCi(config); +}; +// Run it +serve(cli.input[0], cli.flags); diff --git a/dist/lib/base-tools.d.ts b/dist/lib/base-tools.d.ts new file mode 100644 index 0000000..5753428 --- /dev/null +++ b/dist/lib/base-tools.d.ts @@ -0,0 +1,26 @@ +/// +import EventEmitter from 'events'; +import { configOptionType, resolvedPayloadType } from './types'; +declare class BaseTools extends EventEmitter { + protected options: configOptionType; + constructor(options: configOptionType); + /** + * parsing the raw heading and keep them in original format (no lower case) + * @param {*} req request object + * @return {object} + */ + protected parseHeader(req: any): any; + /** + * Extract the json payload + * @param {object} req the request Object + * @return {object} Promise + */ + protected parsePayload(req: any): Promise; + /** + * @param {object} res the respond object unable to get a correct type IncomingMessage? + * @param {string} err error string, this might or might not have, therefore make it optional + * @return {void} nothing + */ + protected resError(res: any, err?: any): void; +} +export { BaseTools, configOptionType }; diff --git a/dist/lib/base-tools.js b/dist/lib/base-tools.js new file mode 100644 index 0000000..16a0e4c --- /dev/null +++ b/dist/lib/base-tools.js @@ -0,0 +1,67 @@ +// src/lib/base-tools +import EventEmitter from 'events'; +class BaseTools extends EventEmitter { + // class constructor + constructor(options) { + super(); + this.options = options; + } + /** + * parsing the raw heading and keep them in original format (no lower case) + * @param {*} req request object + * @return {object} + */ + parseHeader(req) { + const headers = req.rawHeaders; + const ctn = headers.length; + const h = {}; + for (let i = 0; i < ctn; i += 2) { + h[headers[i]] = headers[i + 1]; + } + return h; + } + /** + * Extract the json payload + * @param {object} req the request Object + * @return {object} Promise + */ + parsePayload(req) { + return new Promise((resolver, rejecter) => { + // V.2 here we also need to parse the header and add to the json + // and the result object will become { payload: Object, header: Object } + const header = this.parseHeader(req); + let body = []; + req + .on('data', (chunk) => { + body.push(chunk); + }) + .on('end', () => { + // should catch error here as well + try { + const json = Buffer.concat(body).toString(); + resolver({ + header, + payload: JSON.parse(json) + }); + } + catch (e) { + rejecter(e); + } + }) + .on('error', rejecter); // just throw the rejecter in to handle it + }); + } + /** + * @param {object} res the respond object unable to get a correct type IncomingMessage? + * @param {string} err error string, this might or might not have, therefore make it optional + * @return {void} nothing + */ + resError(res, err) { + res.writeHead(400, { 'content-type': 'application/json' }); + res.end(JSON.stringify({ + error: err + })); + this.emit('error', new Error(err)); + } +} +export { BaseTools }; diff --git a/dist/lib/helpers.d.ts b/dist/lib/helpers.d.ts new file mode 100644 index 0000000..85ec731 --- /dev/null +++ b/dist/lib/helpers.d.ts @@ -0,0 +1,4 @@ +import debug from 'debug'; +export declare const debugFn: (name: string) => debug.Debugger; +export declare const getTimestamp: () => number; +export declare const getRandomInt: (min: number, max: number) => number; diff --git a/dist/lib/helpers.js b/dist/lib/helpers.js new file mode 100644 index 0000000..51cfb95 --- /dev/null +++ b/dist/lib/helpers.js @@ -0,0 +1,8 @@ +// src/lib/helpers.ts +import debug from 'debug'; +// this way we don't need to import everywhere and get back the debug namespace +export const debugFn = (name) => debug(name); +// wrapper to get a timestamp +export const getTimestamp = () => Date.now(); +// return a random number between min and max +export const getRandomInt = (min, max) => (Math.floor(Math.random() * (max - min + 1)) + min); diff --git a/dist/lib/index.d.ts b/dist/lib/index.d.ts new file mode 100644 index 0000000..2fbd5ff --- /dev/null +++ b/dist/lib/index.d.ts @@ -0,0 +1,4 @@ +import createServer from './server'; +export { createServer }; +export { debugFn } from './helpers'; +export { BaseTools, configOptionType } from './base-tools'; diff --git a/dist/lib/index.js b/dist/lib/index.js new file mode 100644 index 0000000..bfaaf81 --- /dev/null +++ b/dist/lib/index.js @@ -0,0 +1,6 @@ +// src/lib/index.ts +// just group the export together +import createServer from './server'; +export { createServer }; +export { debugFn } from './helpers'; +export { BaseTools } from './base-tools'; diff --git a/dist/lib/option.d.ts b/dist/lib/option.d.ts new file mode 100644 index 0000000..c5c5196 --- /dev/null +++ b/dist/lib/option.d.ts @@ -0,0 +1 @@ +export declare const defaultOptions: any; diff --git a/dist/lib/option.js b/dist/lib/option.js new file mode 100644 index 0000000..7aa8a07 --- /dev/null +++ b/dist/lib/option.js @@ -0,0 +1,10 @@ +// src/lib/option.ts +// base config option +// basically all the required options +export const defaultOptions = { + port: 8081, + provider: 'gitee', + path: '/webhook', + branch: 'refs/heads/master', + cmd: 'git pull origin master --no-edit' +}; diff --git a/dist/lib/server.d.ts b/dist/lib/server.d.ts new file mode 100644 index 0000000..248d21a --- /dev/null +++ b/dist/lib/server.d.ts @@ -0,0 +1,9 @@ +/** + * super simple http server using build-in node http + * @param {function} callback + * @param {object} config + * @param {function} debug + * @return {http.Server} + */ +declare function createBareServer(callback: any, config?: any, debug?: any): any; +export default createBareServer; diff --git a/dist/lib/server.js b/dist/lib/server.js new file mode 100644 index 0000000..765d899 --- /dev/null +++ b/dist/lib/server.js @@ -0,0 +1,26 @@ +// src/lib/server.ts +// create a barebone server +import { createServer } from 'http'; +/** + * super simple http server using build-in node http + * @param {function} callback + * @param {object} config + * @param {function} debug + * @return {http.Server} + */ +function createBareServer(callback, config = {}, debug = () => { }) { + const srv = createServer(callback); + if (process.env.NODE_ENV === 'test') { + return srv; + } + if (!config.port) { + throw new Error(`Expect a config.port property!`); + } + return srv.listen(config.port, () => { + try { + debug(`${config.provider} webhook server start @ ${config.port}`); + } + catch (e) { } + }); +} +export default createBareServer; diff --git a/dist/lib/types.d.ts b/dist/lib/types.d.ts new file mode 100644 index 0000000..8160b3f --- /dev/null +++ b/dist/lib/types.d.ts @@ -0,0 +1,15 @@ +declare type configOptionType = { + port: number; + dir?: string; + secret: string; + provider: string; + path: string; + branch: string; + cmd: string; + inited?: boolean; +}; +declare type resolvedPayloadType = { + header: any; + payload: any; +}; +export { configOptionType, resolvedPayloadType }; diff --git a/dist/lib/types.js b/dist/lib/types.js new file mode 100644 index 0000000..5ab2953 --- /dev/null +++ b/dist/lib/types.js @@ -0,0 +1,3 @@ +// src/lib/config-option-type +// just setup a type for the options to use +export {}; diff --git a/dist/main.d.ts b/dist/main.d.ts new file mode 100644 index 0000000..fca5375 --- /dev/null +++ b/dist/main.d.ts @@ -0,0 +1,6 @@ +/** + * Finally the main method + * @param {object} config + * @return {function} for calls + */ +export declare function gitWebhookCi(options: any): any; diff --git a/dist/main.js b/dist/main.js new file mode 100644 index 0000000..d749ae6 --- /dev/null +++ b/dist/main.js @@ -0,0 +1,53 @@ +// src/main.ts it was the main file so rename to main and the function rename as well +import { spawn } from 'child_process'; +import { getProvider } from './provider'; +import { defaultOptions } from './lib/option'; +import { debugFn } from './lib'; +const debug = debugFn('git-webhook-ci:main'); +const blackhole = debugFn('git-webhook-ci:blackhole:main'); +/** + * create a callback to execute + * @param {string} cmd + */ +function createCallback(cmd) { + // the signature just matching the cmd callback and create a problem here + return function callback(payload, opt) { + // don't really want to show this because it's pointless + blackhole(payload); + const ps = spawn(cmd[0], cmd.filter((_, i) => i > 0), opt); + ps.stdout.on('data', data => { + debug("cmd stdout:", data); + }); + ps.stderr.on('data', data => { + debug("cmd stderr:", data); + }); + ps.on('end', code => { + debug(`cmd exited with ${code}`); + }); + }; +} +/** + * Finally the main method + * @param {object} config + * @return {function} for calls + */ +export function gitWebhookCi(options) { + // yeah type safe ... you still need to do validation + if (typeof options !== 'object') { + throw new Error('Expecting options to be an object'); + } + const config = Object.assign({}, defaultOptions, options); + if (!config.secret || config.secret === '') { + throw new Error('You must provide the secret!'); + } + if (typeof config.cmd !== 'string' && typeof config.cmd !== 'function') { + throw new Error('Cmd must be a string or a function!'); + } + debug(config); + const createHandler = getProvider(config.provider); + // Return without Promise, because there is no need to + return createHandler(config, { + env: Object.assign({}, process.env), + cwd: config.dir ? config.dir : process.cwd() + }, typeof config.cmd === 'function' ? config.cmd : createCallback(config.cmd.split(' '))); +} diff --git a/dist/provider/gitee/gitee-handler.d.ts b/dist/provider/gitee/gitee-handler.d.ts new file mode 100644 index 0000000..b72b399 --- /dev/null +++ b/dist/provider/gitee/gitee-handler.d.ts @@ -0,0 +1,24 @@ +import { BaseTools, configOptionType } from '../../lib/base-tools'; +export declare class GiteeHandler extends BaseTools { + constructor(options: configOptionType); + /** + * Main method, the only one that get call + * @param {object} req the request + * @param {object} res the respond + * @param {function} callback res with 404 + * @return {null} nothing + */ + handler(req: any, res: any, callback: any): any; + /** + * Verify the password field + * @param {object} payload Content + * @return {object} promise + */ + private verify; + /** + * @param {object} res the respond + * @param {object} result the payload + * @return {null} nothing + */ + private resSuccess; +} diff --git a/dist/provider/gitee/gitee-handler.js b/dist/provider/gitee/gitee-handler.js new file mode 100644 index 0000000..8d9f764 --- /dev/null +++ b/dist/provider/gitee/gitee-handler.js @@ -0,0 +1,74 @@ +// src/provider/gitee/gitee-class.ts +import { BaseTools } from '../../lib/base-tools'; +import { verifyHandler } from './verify'; +import { debugFn } from '../../lib/helpers'; +const debug = debugFn('git-webhook-ci:gitee:handler'); +export class GiteeHandler extends BaseTools { + constructor(options) { + super(options); + } + /** + * Main method, the only one that get call + * @param {object} req the request + * @param {object} res the respond + * @param {function} callback res with 404 + * @return {null} nothing + */ + handler(req, res, callback) { + debug(`got call here`); + if (req.method !== 'POST' || req.url.split('?').shift() !== this.options.path) { + debug(req.url); + debug(this.options.path); + return callback(); // This is the only time we use the callback + } + this.parsePayload(req) + .then(obj => { + this.verify(obj) + .then(result => { + this.resSuccess(res, req, result); + }) + .catch((err) => { + this.resError(res, err); + }); + }); + } + /** + * Verify the password field + * @param {object} payload Content + * @return {object} promise + */ + verify(obj) { + return new Promise((resolver, rejecter) => { + const { header, payload } = obj; + if (verifyHandler(header, this.options.secret)) { + resolver(payload); + } + else { + rejecter(new Error('Gitee verify failed')); + } + }); + } + /** + * @param {object} res the respond + * @param {object} result the payload + * @return {null} nothing + */ + resSuccess(res, req, result) { + res.writeHead(200, { 'content-type': 'application/json' }); + res.end('{"ok":true}'); + // Check the result if this is what we wanted + if (result.hook_name === 'push_hooks') { // @TODO check if this is still correct + this.emit('push', { + payload: result, + host: req.headers.host, + event: result.hook_name + }); + } + else { + this.emit('error', { + msg: 'Not the event we are expecting', + event: result.hook_name + }); + } + } +} diff --git a/dist/provider/gitee/index.d.ts b/dist/provider/gitee/index.d.ts new file mode 100644 index 0000000..5872b47 --- /dev/null +++ b/dist/provider/gitee/index.d.ts @@ -0,0 +1,11 @@ +import { configOptionType } from '../../lib'; +/** + * The main method to handle the server create and run the whole service for gitee + * @param {configOptionType} config for the overall setup of the system + * @param {object} opt this provide the environment variables to the cmd to execute later + * @param {function} callback + * @param {function} errorHandler optional + * @return {http server instance} + */ +declare function createGiteeServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; +export default createGiteeServer; diff --git a/dist/provider/gitee/index.js b/dist/provider/gitee/index.js new file mode 100644 index 0000000..1a0d2dd --- /dev/null +++ b/dist/provider/gitee/index.js @@ -0,0 +1,42 @@ +// src/provider/gitee/index.ts +// the actual execution +import { GiteeHandler } from './gitee-handler'; +import { createServer, debugFn } from '../../lib'; +const debug = debugFn('git-webhook-ci:gitee'); +/** + * The main method to handle the server create and run the whole service for gitee + * @param {configOptionType} config for the overall setup of the system + * @param {object} opt this provide the environment variables to the cmd to execute later + * @param {function} callback + * @param {function} errorHandler optional + * @return {http server instance} + */ +function createGiteeServer(config, opt, callback, errorHandler = () => { }) { + const gitee = new GiteeHandler(config); + // just debug it out + gitee.on('error', (err) => { + debug('ERROR', err); + errorHandler(err); + }); + gitee.on('push', (result) => { + const ref = result.payload.ref; // ref is the branch name + if (config.branch === '*' || config.branch === ref) { + callback(result, opt, ref); + } + else { + errorHandler(ref); + debug('Gitee webhook is not expecting this branch', ref); + } + }); + // return the server instance + return createServer((req, res) => { + debug(`server callback executed`); + gitee.handler(req, res, (err) => { + debug('The url got called! [%s]', req.url, err); + errorHandler(req.url, err); + res.statusCode = 404; + res.end('-- no such location --'); + }); + }, config, debug); +} +export default createGiteeServer; diff --git a/dist/provider/gitee/verify.d.ts b/dist/provider/gitee/verify.d.ts new file mode 100644 index 0000000..659f63b --- /dev/null +++ b/dist/provider/gitee/verify.d.ts @@ -0,0 +1,13 @@ +/** + * create the secret key to compare + * @param {string} secretKey the secret key set during sestting up the webhook + * @param {number} timestamp this timestamp send from the git provider server + */ +export declare function getToken(secretKey: string, timestamp: number): string; +/** + * gitee has it's own payload structure therefore we need to check if we have those things in the header + * @param {*} header the parsed header from req + * @param {string} secretKey the secret key provided when setup the webhook + * @return {boolean} + */ +export declare function verifyHandler(header: any, secretKey: string): boolean; diff --git a/dist/provider/gitee/verify.js b/dist/provider/gitee/verify.js new file mode 100644 index 0000000..8ca1491 --- /dev/null +++ b/dist/provider/gitee/verify.js @@ -0,0 +1,40 @@ +// src/provider/gitee/secret.ts +// see here: https://gitee.com/help/articles/4290#article-header3 +import { createHmac } from 'crypto'; +// use the debug to find out what went wrong +import { debugFn } from '../../lib/helpers'; +const debug = debugFn('git-webhook-ci:gitee:verify'); +/** + * create the secret key to compare + * @param {string} secretKey the secret key set during sestting up the webhook + * @param {number} timestamp this timestamp send from the git provider server + */ +export function getToken(secretKey, timestamp) { + const secret_enc = Buffer.from(secretKey, 'utf8'); + const string_to_sign = `${timestamp}\n${secret_enc}`; + const hmac = createHmac('sha256', secret_enc); + const data = hmac.update(string_to_sign); + return encodeURIComponent(data.digest('base64')); +} +/** + * gitee has it's own payload structure therefore we need to check if we have those things in the header + * @param {*} header the parsed header from req + * @param {string} secretKey the secret key provided when setup the webhook + * @return {boolean} + */ +export function verifyHandler(header, secretKey) { + if (header['User-Agent'] === 'git-oschina-hook') { + debug('User-Agent passed', header['User-Agent']); + const expected = ['X-Gitee-Token', 'X-Gitee-Timestamp', 'X-Gitee-Event'].filter(key => header[key] !== undefined).length; + if (expected === 3) { + debug('Expected header passed'); + if (header['X-Gitee-Token'] === getToken(secretKey, parseInt(header['X-Gitee-Timestamp']))) { + return true; + } + else { + debug('verify the x-gitee-token failed'); + } + } + } + return false; +} diff --git a/dist/provider/github/index.d.ts b/dist/provider/github/index.d.ts new file mode 100644 index 0000000..8c5a2a9 --- /dev/null +++ b/dist/provider/github/index.d.ts @@ -0,0 +1,3 @@ +import { configOptionType } from '../../lib'; +declare function createGithubServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; +export default createGithubServer; diff --git a/dist/provider/github/index.js b/dist/provider/github/index.js new file mode 100644 index 0000000..8014317 --- /dev/null +++ b/dist/provider/github/index.js @@ -0,0 +1,37 @@ +// src/provider/github/index.ts +// Github is using another external npm to handle it +import { createServer, debugFn } from '../../lib'; +import githubWebhook from 'github-webhook-handler'; +const debug = debugFn('git-webhook-ci:github'); +// main method +function createGithubServer(config, opt, callback, errorHandler = () => { }) { + const handler = githubWebhook({ + path: config.path, + secret: config.secret + }); + handler.on('error', err => { + errorHandler(err); + debug('Error:', err.message); + }); + // On received push event + handler.on('push', result => { + const ref = result.payload.ref; + if (config.branch === '*' || config.branch === ref) { + callback(result, opt, ref); + } + else { + const errorStr = `Received a push event for ${result.payload.repository.name} to ${ref}`; + debug(errorStr); + errorHandler(errorStr); + } + }); + return createServer((req, res) => { + handler(req, res, (err) => { + debug(`The url got called! ${req.url}`, err); + errorHandler(err); + res.statusCode = 404; + res.end('-- no such location --'); + }); + }, config, debug); +} +export default createGithubServer; diff --git a/dist/provider/gitlab/gitlab-handler.d.ts b/dist/provider/gitlab/gitlab-handler.d.ts new file mode 100644 index 0000000..36f5aa6 --- /dev/null +++ b/dist/provider/gitlab/gitlab-handler.d.ts @@ -0,0 +1,26 @@ +import { BaseTools, configOptionType } from '../../lib/base-tools'; +export declare class GitlabHandler extends BaseTools { + constructor(options: configOptionType); + /** + * Main method + * @param {object} req the request + * @param {object} res the respond + * @param {function} callback res with 404 + * @param {function} errorHandler optional error callback + * @return {null} nothing + */ + handler(req: any, res: any, callback: any): any; + /** + * Verify the password field + * @param {object} payload Content + * @param {object} headers headers looking for the X-Gitlab-Event: Push Hook + * @return {object} promise + */ + private verify; + /** + * @param {object} res the respond + * @param {object} result the payload + * @return {null} nothing + */ + private resSuccess; +} diff --git a/dist/provider/gitlab/gitlab-handler.js b/dist/provider/gitlab/gitlab-handler.js new file mode 100644 index 0000000..f546d68 --- /dev/null +++ b/dist/provider/gitlab/gitlab-handler.js @@ -0,0 +1,73 @@ +// src/provider/gitlab/gitlab-handler.ts +import { BaseTools } from '../../lib/base-tools'; +export class GitlabHandler extends BaseTools { + constructor(options) { + super(options); + } + /** + * Main method + * @param {object} req the request + * @param {object} res the respond + * @param {function} callback res with 404 + * @param {function} errorHandler optional error callback + * @return {null} nothing + */ + handler(req, res, callback) { + if (req.url.split('?').shift() !== this.options.path || req.method !== 'POST') { + return callback(); // The only time we use the callback + } + this.parsePayload(req).then(payload => { + const headers = req.headers; // JSON.stringify(req.headers, replacer); + // cache = null; + this.verify(payload, headers) + .then(payload => { + this.resSuccess(res, req, payload); + }) + .catch((err) => { + this.resError(res, err); + }); + }); + } + /** + * Verify the password field + * @param {object} payload Content + * @param {object} headers headers looking for the X-Gitlab-Event: Push Hook + * @return {object} promise + */ + verify(payload, headers) { + const eventName = 'X-Gitlab-Event'.toLowerCase(); + const token = 'X-Gitlab-Token'.toLowerCase(); + // Console.log('headers', headers, typeof headers); + return new Promise((resolver, rejecter) => { + if (headers[eventName] === 'Push Hook' && headers[token] === this.options.secret) { + resolver(payload); + } + else { + rejecter(new Error('Verify failed')); + } + }); + } + /** + * @param {object} res the respond + * @param {object} result the payload + * @return {null} nothing + */ + resSuccess(res, req, payload) { + res.writeHead(200, { 'content-type': 'application/json' }); + res.end('{"ok":true}'); + // Check the result if this is what we wanted + if (payload.object_kind === 'push') { + this.emit('push', { + payload: payload, + host: req.headers.host, + event: payload.object_kind + }); + } + else { + this.emit('error', { + msg: 'Not the event we are expecting', + event: payload.object_kind + }); + } + } +} diff --git a/dist/provider/gitlab/index.d.ts b/dist/provider/gitlab/index.d.ts new file mode 100644 index 0000000..a708d6c --- /dev/null +++ b/dist/provider/gitlab/index.d.ts @@ -0,0 +1,3 @@ +import { configOptionType } from '../../lib'; +declare function createGitlabServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; +export default createGitlabServer; diff --git a/dist/provider/gitlab/index.js b/dist/provider/gitlab/index.js new file mode 100644 index 0000000..6724c16 --- /dev/null +++ b/dist/provider/gitlab/index.js @@ -0,0 +1,33 @@ +// src/provider/gitlab/index.ts +import { GitlabHandler } from './gitlab-handler'; +import { createServer, debugFn } from '../../lib'; +const debug = debugFn('git-webhook-ci:gitlab'); +// main method +function createGitlabServer(config, opt, callback, errorHandler = () => { }) { + const gitlab = new GitlabHandler(config); + gitlab.on('error', (err) => { + debug('error', err); + errorHandler(err); + }); + // Listen on the push event - success + gitlab.on('push', (result) => { + const ref = result.payload.ref; + if (config.branch === '*' || config.branch === ref) { + callback(result, opt, ref); + } + else { + errorHandler(ref); + debug('Gitee webhook is not expecting this branch', ref); + } + }); + // return the http server + return createServer((req, res) => { + gitlab.handler(req, res, (err) => { + debug('The url got called! [%s]', req.url, err); + errorHandler(err); + res.statusCode = 404; + res.end('-- no such location --'); + }); + }, config, debug); +} +export default createGitlabServer; diff --git a/dist/provider/index.d.ts b/dist/provider/index.d.ts new file mode 100644 index 0000000..9d40430 --- /dev/null +++ b/dist/provider/index.d.ts @@ -0,0 +1,7 @@ +/** + * Wrap all the configuration check code here + * Then init the instance and return it + * @NOTE v.2 gitee now is the default + * also it's now a named export + */ +export declare function getProvider(provider: string): any; diff --git a/dist/provider/index.js b/dist/provider/index.js new file mode 100644 index 0000000..eb576a7 --- /dev/null +++ b/dist/provider/index.js @@ -0,0 +1,25 @@ +// src/provider/index.ts +// group everything together and call from here +import giteeWebhook from './gitee'; +import githubWebhook from './github'; +import gitlabWebhook from './gitlab'; +import wechatWebhook from './wechat'; +/** + * Wrap all the configuration check code here + * Then init the instance and return it + * @NOTE v.2 gitee now is the default + * also it's now a named export + */ +export function getProvider(provider) { + switch (provider) { + case 'wechat': + return wechatWebhook; + case 'gitlab': + return gitlabWebhook; + case 'github': + return githubWebhook; + case 'gitee': + default: + return giteeWebhook; + } +} diff --git a/dist/provider/wechat/index.d.ts b/dist/provider/wechat/index.d.ts new file mode 100644 index 0000000..c37373c --- /dev/null +++ b/dist/provider/wechat/index.d.ts @@ -0,0 +1,3 @@ +import { configOptionType } from '../../lib'; +declare function createWechatServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; +export default createWechatServer; diff --git a/dist/provider/wechat/index.js b/dist/provider/wechat/index.js new file mode 100644 index 0000000..e3dcbe3 --- /dev/null +++ b/dist/provider/wechat/index.js @@ -0,0 +1,24 @@ +// src/provider/wechat/index.ts +import { WechatHandler } from './wechat-handler'; +import { createServer, debugFn } from '../../lib'; +const debug = debugFn('git-webhook-ci:wechat'); +function createWechatServer(config, opt, callback, errorHandler = () => { }) { + const wechat = new WechatHandler(config); + /* This is not implemented, there is no need at the moment + wechat.on('error', err => { + debug('error', err); + }); + */ + wechat.on('push', result => { + callback(result, opt); + }); + return createServer((req, res) => { + wechat.handler(req, res, (err) => { + errorHandler(err); + debug('The url got called! [%s]', req.url, err); + res.statusCode = 404; + res.end('-- no such location --'); + }); + }, config, debug); +} +export default createWechatServer; diff --git a/dist/provider/wechat/wechat-handler.d.ts b/dist/provider/wechat/wechat-handler.d.ts new file mode 100644 index 0000000..40d2049 --- /dev/null +++ b/dist/provider/wechat/wechat-handler.d.ts @@ -0,0 +1,22 @@ +/** + * This is for wechat mini-app push callback + * Based on their PHP version + */ +import { BaseTools, configOptionType } from '../../lib'; +export declare class WechatHandler extends BaseTools { + constructor(options: configOptionType); + /** + * Main interface, this is different from the other because there is no filter + * on what is coming, just verify it then pass the payload to the callback + */ + handler(req: any, res: any, callback: any): void; + private getParams; + /** + * This is different using the query parameter to compare + * Another thing is - this is a one off verify process + * once the wechat end verify this end is correct, it will + * just send data over. Need to figure out a way to run this + * verify before the actual listening + */ + private verify; +} diff --git a/dist/provider/wechat/wechat-handler.js b/dist/provider/wechat/wechat-handler.js new file mode 100644 index 0000000..3b06904 --- /dev/null +++ b/dist/provider/wechat/wechat-handler.js @@ -0,0 +1,86 @@ +/** + * This is for wechat mini-app push callback + * Based on their PHP version + */ +/* +https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/callback_help.html + +private function checkSignature() +{ + $signature = $_GET["signature"]; + $timestamp = $_GET["timestamp"]; + $nonce = $_GET["nonce"]; + + $token = TOKEN; + $tmpArr = array($token, $timestamp, $nonce); + sort($tmpArr, SORT_STRING); + $tmpStr = implode( $tmpArr ); + $tmpStr = sha1( $tmpStr ); + + if( $tmpStr == $signature ){ + return true; + }else{ + return false; + } +} +*/ +import { BaseTools, debugFn } from '../../lib'; +import { URL } from 'url'; +import sha1 from 'sha1'; +const debug = debugFn('git-webhook-ci:wechat'); +export class WechatHandler extends BaseTools { + constructor(options) { + super(options); + } + /** + * Main interface, this is different from the other because there is no filter + * on what is coming, just verify it then pass the payload to the callback + */ + handler(req, res, callback) { + if (this.options.inited !== true) { + const echostr = this.verify(req); + if (!echostr) { + debug('verify with wechat server failed'); + return callback('verify failed'); + } + debug(`verify with wechat echostr '${echostr}' correct`); + res.writeHead(200, { 'content-type': 'text/html' }); + res.end(echostr); + return; + } + // The implementation is different + this.parsePayload(req) + .then(payload => { + res.writeHead(200, { 'content-type': 'application/json' }); + res.end('{"ok": true}'); + // Just reuse the same naming + this.emit('push', { + payload, + host: req.headers.host, + event: 'wechat-push' + }); + }); + } + // get the params from url + getParams(url) { + const u = new URL(url); + const params = ['signature', 'timestamp', 'nonce', 'echostr']; + return params.map((param) => { + return { [param]: u.searchParams.get(param) }; + }).reduce((a, b) => Object.assign(a, b), {}); + } + /** + * This is different using the query parameter to compare + * Another thing is - this is a one off verify process + * once the wechat end verify this end is correct, it will + * just send data over. Need to figure out a way to run this + * verify before the actual listening + */ + verify(req) { + const { signature, timestamp, nonce, echostr } = this.getParams(req.url); + const $token = this.options.secret; + let $tmpArr = [$token, timestamp, nonce]; + $tmpArr.sort(); + return sha1($tmpArr.join('')) === signature ? echostr : false; + } +} diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index ae9736b..579a9cb 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -16,10 +16,10 @@ class BaseTools extends EventEmitter { * @param {*} req request object * @return {object} */ - protected parseHeader(req): any { - const headers = req.rawHeaders - const ctn = headers.length - const h = {} + protected parseHeader(req: any): any { + const headers: Array = req.rawHeaders + const ctn: number = headers.length + const h: any = {} for (let i = 0; i < ctn; i += 2) { h[ headers[i] ] = headers[ i + 1] } diff --git a/src/lib/option.ts b/src/lib/option.ts index 1733acc..9852e84 100644 --- a/src/lib/option.ts +++ b/src/lib/option.ts @@ -1,10 +1,8 @@ // src/lib/option.ts // base config option -import configOptionType from './config-option-type' - -// basically all the required options -export const defaultOptions: configOptionType = { +// basically all the required options +export const defaultOptions: any = { port: 8081, provider: 'gitee', path: '/webhook', diff --git a/src/lib/types.ts b/src/lib/types.ts index df6e951..4b67f59 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -4,7 +4,7 @@ type configOptionType = { port: number, dir?: string, - secret?: string, + secret: string, provider: string, path: string, branch: string, diff --git a/src/main.ts b/src/main.ts index 24f8b73..3a86cdb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,15 +7,20 @@ import { defaultOptions } from './lib/option' import { debugFn } from './lib' const debug = debugFn('git-webhook-ci:main') - +const blackhole = debugFn('git-webhook-ci:blackhole:main') /** * create a callback to execute * @param {string} cmd */ function createCallback(cmd: Array): any { + // the signature just matching the cmd callback and create a problem here return function callback(payload: any, opt: any) { - const ps = spawn(cmd[0], cmd.filter((c: any, i: number): boolean => i > 0), opt) + // don't really want to show this because it's pointless + blackhole(payload) + + const ps = spawn(cmd[0], cmd.filter((_: any, i: number): boolean => i > 0), opt) + ps.stdout.on('data', data => { debug("cmd stdout:", data) }) -- Gitee From 0b91656c86375eb55e885d5478954730b28122f5 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Sat, 19 Jun 2021 08:08:11 +0800 Subject: [PATCH 12/21] remove that blackhole call --- src/main.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index 3a86cdb..4fee6f1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,7 +7,6 @@ import { defaultOptions } from './lib/option' import { debugFn } from './lib' const debug = debugFn('git-webhook-ci:main') -const blackhole = debugFn('git-webhook-ci:blackhole:main') /** * create a callback to execute @@ -15,9 +14,7 @@ const blackhole = debugFn('git-webhook-ci:blackhole:main') */ function createCallback(cmd: Array): any { // the signature just matching the cmd callback and create a problem here - return function callback(payload: any, opt: any) { - // don't really want to show this because it's pointless - blackhole(payload) + return function callback(_: any, opt: any) { const ps = spawn(cmd[0], cmd.filter((_: any, i: number): boolean => i > 0), opt) -- Gitee From ee98effcd6b4d83e3336eb150a3d404faa332615 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Sat, 19 Jun 2021 13:11:45 +0800 Subject: [PATCH 13/21] remove the cli --- README.md | 190 ++--------------------- dist/cli.d.ts | 2 - dist/cli.js | 79 ---------- dist/lib/base-tools.d.ts | 26 ---- dist/lib/base-tools.js | 67 -------- dist/lib/helpers.d.ts | 4 - dist/lib/helpers.js | 8 - dist/lib/index.d.ts | 4 - dist/lib/index.js | 6 - dist/lib/option.d.ts | 1 - dist/lib/option.js | 10 -- dist/lib/server.d.ts | 9 -- dist/lib/server.js | 26 ---- dist/lib/types.d.ts | 15 -- dist/lib/types.js | 3 - dist/main.d.ts | 6 - dist/main.js | 53 ------- dist/provider/gitee/gitee-handler.d.ts | 24 --- dist/provider/gitee/gitee-handler.js | 74 --------- dist/provider/gitee/index.d.ts | 11 -- dist/provider/gitee/index.js | 42 ----- dist/provider/gitee/verify.d.ts | 13 -- dist/provider/gitee/verify.js | 40 ----- dist/provider/github/index.d.ts | 3 - dist/provider/github/index.js | 37 ----- dist/provider/gitlab/gitlab-handler.d.ts | 26 ---- dist/provider/gitlab/gitlab-handler.js | 73 --------- dist/provider/gitlab/index.d.ts | 3 - dist/provider/gitlab/index.js | 33 ---- dist/provider/index.d.ts | 7 - dist/provider/index.js | 25 --- dist/provider/wechat/index.d.ts | 3 - dist/provider/wechat/index.js | 24 --- dist/provider/wechat/wechat-handler.d.ts | 22 --- dist/provider/wechat/wechat-handler.js | 86 ---------- src/cli.ts => docs/02-cli.md | 21 ++- src/lib/option.ts | 3 + 37 files changed, 25 insertions(+), 1054 deletions(-) delete mode 100644 dist/cli.d.ts delete mode 100644 dist/cli.js delete mode 100644 dist/lib/base-tools.d.ts delete mode 100644 dist/lib/base-tools.js delete mode 100644 dist/lib/helpers.d.ts delete mode 100644 dist/lib/helpers.js delete mode 100644 dist/lib/index.d.ts delete mode 100644 dist/lib/index.js delete mode 100644 dist/lib/option.d.ts delete mode 100644 dist/lib/option.js delete mode 100644 dist/lib/server.d.ts delete mode 100644 dist/lib/server.js delete mode 100644 dist/lib/types.d.ts delete mode 100644 dist/lib/types.js delete mode 100644 dist/main.d.ts delete mode 100644 dist/main.js delete mode 100644 dist/provider/gitee/gitee-handler.d.ts delete mode 100644 dist/provider/gitee/gitee-handler.js delete mode 100644 dist/provider/gitee/index.d.ts delete mode 100644 dist/provider/gitee/index.js delete mode 100644 dist/provider/gitee/verify.d.ts delete mode 100644 dist/provider/gitee/verify.js delete mode 100644 dist/provider/github/index.d.ts delete mode 100644 dist/provider/github/index.js delete mode 100644 dist/provider/gitlab/gitlab-handler.d.ts delete mode 100644 dist/provider/gitlab/gitlab-handler.js delete mode 100644 dist/provider/gitlab/index.d.ts delete mode 100644 dist/provider/gitlab/index.js delete mode 100644 dist/provider/index.d.ts delete mode 100644 dist/provider/index.js delete mode 100644 dist/provider/wechat/index.d.ts delete mode 100644 dist/provider/wechat/index.js delete mode 100644 dist/provider/wechat/wechat-handler.d.ts delete mode 100644 dist/provider/wechat/wechat-handler.js rename src/cli.ts => docs/02-cli.md (85%) diff --git a/README.md b/README.md index 2d330e1..84a13ac 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# git-webhook-ci [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] +# git-webhook-ci > A Git (github/gitee) webhook callback server to fetch new code (poor man CI) This little tool is born out of real projects. Keep having to deploy and setup demo site etc. Why bother if you own the git account? @@ -23,147 +23,18 @@ Create a js file (normally on your project root directory). Let's call it `webho ```js const gitWebhook = require('git-webhook-ci'); const config = { + "provider": "github", // from version 2 you MUST provide this "secret": "your-github-webhook-secret", "path": "/webhook", "port": 8081, "branch": "refs/heads/master", // New in 0.4.1 you can pass * wildcard to listen to all branches - "cmd": "git pull origin master --no-edit" -}; - -gitWebhook(config); -``` - -*Important change, we no longer support github by default. Instead if you don't add the provider view, it will be gitlab!* - - -The minimum setup can be like this: - -```js - - // Default is gitlab - gitWebhook({secret: "your-gitlab-token"}); - - // For Gitee - gitWebhook({secret: 'your-gitee-password', provider: 'gitee'}); - - // For Github - gitWebhook({secret: 'your-github-webhook-secret', provider: 'github'}); - -``` - - -## New in 0.4.0 - cmd accept (String) command to run or (Function) callback - -The `cmd` config option now accept a function. - -The signature as follow - -```js -{ - secret: 'your-secret-between-you-and-gitlab', - cmd: (result, opt, ref) => { - // result has 3 properties - // 1. payload - // 2. host - // 3. event - from github / gitee - // opt is an environment variable that you can pass to the spawn + "cmd": "git pull origin master --no-edit", + "error": (err) => { + // do thing with your error } } -``` - -Example how to combine the wildcard branch option, and a function callback - -```js -const gitWebhook = require('git-webook-ci'); -const { spawn } = require('child_process'); - -const server = gitWebhook({ - secret: 'your-secret-between-you-and-github', - branch: '*', - cmd: (result, opt, ref) => { - switch (ref) { - case 'refs/heads/master': - const e1 = spawn('npm', ['run', 'something'], opt); - break; - case 'refs/heads/develop': - const e2 = spawn('npm', ['run', 'something-else'], opt); - break; - default: - // do special stuff using the result object - specialFunc(result.payload, opt); - } - } -}); - -``` - -As you can see from the code example from above. The method `gitWebhook` actually return the -`server` instance from `http.createServer`. So you can make it to stop, restart etc easily. - -## New in 0.4.x - support 码云 Gitee.com - -You can now pass a new configuration option `provider`: -```js -{ - secret: 'your-password-between-you-and-gitee', - provider: 'gitee' -} -``` - -## New in 0.5.x - support Gitlab.com - -You just need to change the provider to `gitlab`: (*This is the default since V.0.9.x*) - -```js -{ - secret: 'your-gitlab-token', - provider: 'gitlab' -} -``` - -## New in 0.8.x - support 微信小程序消息服务 Wechat mini app callback -We have added a new provider here - it's not a git repo. It supports the [Wechat callback](https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/callback_help.html). - -**There are several different between wechat callback and the other providers** - -There is a new property that you need to supply when you init your webhook with Wechat. -Because this is a two step process. Once your server is verify with Wechat server. -They will just push data over to the url. So you need to run this once like so. - -First you need to run with the `inited:false` (default) - -```js -{ - secret: 'the-token-you-setup-with-wechat', - provider: 'wechat', - inited: false // this is default -} -``` - -Then re-config your webhook to run normal operation: - -```js -{ - secret: 'the-token-you-setup-with-wechat', - provider: 'wechat', - inited: true // default: false -} -``` - -There is a complete example in the *Wiki coming back soon* to demonstrate how you can do this automatically, -with additional module `fs-extra`, `nodemon` and `node-config`. - ---- - -If you are using function as your `cmd` property, there will only be two parameters supply, when execute your callback. - -```js - { - cmd: (result, opt) => { - // there is no ref - } - } +gitWebhook(config); ``` ### Full configuration properties @@ -176,6 +47,7 @@ If you are using function as your `cmd` property, there will only be two paramet | port | The port number where this callback server running on | `8081` | Integer | | branch | The branch where you will trigger action when received event from github. You can pass `*` wildcard to listen to all the branches | `refs/heads/master` | String | | cmd | The command to execute when callback happens. You can also pass this as a function (see above for signature) and especially useful when you use `*` for branch | `git pull origin master --no-edit` | String | +| error | expect a function and you can handle the error yourself. Or enable DEBUG=git-webhook-ci:error to see the error | | inited | only available with `wechat` provider | `false` | Boolean | ### Debug option @@ -189,63 +61,25 @@ Internally we use `debug` to track what's going on. So you can just pass the env If you do that, you will see a huge amount of info. All our debug flags are prefixed with `git-webhook-ci`, and here is the list of all the keys we use in this npm. -- git-webhook-ci:main +- git-webhook-ci:error - This is the most likely you will use, to see the error message +- git-webhook-ci:main - You will see the configuration option being pass to the main method - git-webhook-ci:gitlab - git-webhook-ci:github - git-webhook-ci:gitee - git-webhook-ci:wechat -- git-webhook-ci:demo (only in test) -- git-webhook-ci:test For example: ```sh - DEBUG=git-webhook-ci:main,git-webhook-ci:wechat node ./webhook.js + DEBUG=git-webhook-ci:main,git-webhook-ci:error node ./webhook.js ``` Then you will only see the main (top interface) and the Wechat internal debug messages. ## CLI -You can install this tools globally. - -```sh - $ npm install git-webhook-ci --global -``` - -Then you can call it from command line like so - -```sh - $ git-webhook-ci /path/to/your/git --secret secret-you-setup - -``` - -Or in your `package.json` - -```js - { - "scripts": { - "webhook": "git-webhook-ci ./ --secret secret-you-setup" - } - } -``` - -Then just run it with `npm run webhook` - -## HOW TO - -Check our *Wiki will be back shortly* for more information about how to setup your app. +We drop the cli support since V.2 due to the large amount of configuration options. You can try to write your own follow [this example](docs/02-cli.md) ## License -MIT © [NEWBRAN.CH](newbran.ch) - - -[npm-image]: https://badge.fury.io/js/git-webhook-ci.svg -[npm-url]: https://npmjs.org/package/git-webhook-ci -[travis-image]: https://travis-ci.org/NewbranLTD/git-webhook-ci.svg?branch=master -[travis-url]: https://travis-ci.org/NewbranLTD/git-webhook-ci -[daviddm-image]: https://david-dm.org/NewbranLTD/git-webhook-ci.svg?theme=shields.io -[daviddm-url]: https://david-dm.org/NewbranLTD/git-webhook-ci - -Power by [generator-nodex](https://gitlab.com/newbranltd/generator-nodex). +WTFPL - Joel Chu (c) 2021 diff --git a/dist/cli.d.ts b/dist/cli.d.ts deleted file mode 100644 index b798801..0000000 --- a/dist/cli.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -export {}; diff --git a/dist/cli.js b/dist/cli.js deleted file mode 100644 index 0174bf9..0000000 --- a/dist/cli.js +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env node -/** - * Run from cli - * dir: '', - * path: '/webhook', - * port: 8081, - * branch: 'refs/heads/master', - * cmd: 'git pull origin master --no-edit' - */ -// import { configOptionType } from './lib' -import { gitWebhookCi } from './main'; -import meow from 'meow'; -const helpText = ` - Usage - $ node git-webhook-ci - - $ node git-webhook-ci --secret secret-from-github - - $ node git-webhook-ci --secret secret-from-github --cmd 'git pull origin develop' - - For wechat - - $ node git-webhook-ci -secret wechat-token --inited true --cmd 'some cmd' -`; -const flags = { - port: { - type: 'number', - alias: 'po', - default: 8081 - }, - provider: { - type: 'string', - alias: 'pr', - default: 'github' - }, - dir: { - type: 'string', - alias: 'd' - }, - path: { - type: 'string', - alias: 'p', - default: '/webhook' - }, - branch: { - type: 'string', - alias: 'b', - default: 'refs/heads/master' - }, - secret: { - type: 'string', - alias: 's', - default: '' - }, - cmd: { - type: 'string', - alias: 'c', - default: 'git pull origin master --no-edit' - }, - inited: { - type: 'boolean', - alias: 'i', - default: false - } -}; -const cli = meow(helpText, { - flags, - importMeta: import.meta // TS was complainting about this ... -}); -// Wrap into a method to call -const serve = function (p, flags) { - if (!p || p === '') { - throw new Error('You must provide the ! Check usage for more detail.'); - } - const config = Object.assign({ __caller__: 'meow' }, { dir: p }, flags); - return gitWebhookCi(config); -}; -// Run it -serve(cli.input[0], cli.flags); diff --git a/dist/lib/base-tools.d.ts b/dist/lib/base-tools.d.ts deleted file mode 100644 index 5753428..0000000 --- a/dist/lib/base-tools.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -/// -import EventEmitter from 'events'; -import { configOptionType, resolvedPayloadType } from './types'; -declare class BaseTools extends EventEmitter { - protected options: configOptionType; - constructor(options: configOptionType); - /** - * parsing the raw heading and keep them in original format (no lower case) - * @param {*} req request object - * @return {object} - */ - protected parseHeader(req: any): any; - /** - * Extract the json payload - * @param {object} req the request Object - * @return {object} Promise - */ - protected parsePayload(req: any): Promise; - /** - * @param {object} res the respond object unable to get a correct type IncomingMessage? - * @param {string} err error string, this might or might not have, therefore make it optional - * @return {void} nothing - */ - protected resError(res: any, err?: any): void; -} -export { BaseTools, configOptionType }; diff --git a/dist/lib/base-tools.js b/dist/lib/base-tools.js deleted file mode 100644 index 16a0e4c..0000000 --- a/dist/lib/base-tools.js +++ /dev/null @@ -1,67 +0,0 @@ -// src/lib/base-tools -import EventEmitter from 'events'; -class BaseTools extends EventEmitter { - // class constructor - constructor(options) { - super(); - this.options = options; - } - /** - * parsing the raw heading and keep them in original format (no lower case) - * @param {*} req request object - * @return {object} - */ - parseHeader(req) { - const headers = req.rawHeaders; - const ctn = headers.length; - const h = {}; - for (let i = 0; i < ctn; i += 2) { - h[headers[i]] = headers[i + 1]; - } - return h; - } - /** - * Extract the json payload - * @param {object} req the request Object - * @return {object} Promise - */ - parsePayload(req) { - return new Promise((resolver, rejecter) => { - // V.2 here we also need to parse the header and add to the json - // and the result object will become { payload: Object, header: Object } - const header = this.parseHeader(req); - let body = []; - req - .on('data', (chunk) => { - body.push(chunk); - }) - .on('end', () => { - // should catch error here as well - try { - const json = Buffer.concat(body).toString(); - resolver({ - header, - payload: JSON.parse(json) - }); - } - catch (e) { - rejecter(e); - } - }) - .on('error', rejecter); // just throw the rejecter in to handle it - }); - } - /** - * @param {object} res the respond object unable to get a correct type IncomingMessage? - * @param {string} err error string, this might or might not have, therefore make it optional - * @return {void} nothing - */ - resError(res, err) { - res.writeHead(400, { 'content-type': 'application/json' }); - res.end(JSON.stringify({ - error: err - })); - this.emit('error', new Error(err)); - } -} -export { BaseTools }; diff --git a/dist/lib/helpers.d.ts b/dist/lib/helpers.d.ts deleted file mode 100644 index 85ec731..0000000 --- a/dist/lib/helpers.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import debug from 'debug'; -export declare const debugFn: (name: string) => debug.Debugger; -export declare const getTimestamp: () => number; -export declare const getRandomInt: (min: number, max: number) => number; diff --git a/dist/lib/helpers.js b/dist/lib/helpers.js deleted file mode 100644 index 51cfb95..0000000 --- a/dist/lib/helpers.js +++ /dev/null @@ -1,8 +0,0 @@ -// src/lib/helpers.ts -import debug from 'debug'; -// this way we don't need to import everywhere and get back the debug namespace -export const debugFn = (name) => debug(name); -// wrapper to get a timestamp -export const getTimestamp = () => Date.now(); -// return a random number between min and max -export const getRandomInt = (min, max) => (Math.floor(Math.random() * (max - min + 1)) + min); diff --git a/dist/lib/index.d.ts b/dist/lib/index.d.ts deleted file mode 100644 index 2fbd5ff..0000000 --- a/dist/lib/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -import createServer from './server'; -export { createServer }; -export { debugFn } from './helpers'; -export { BaseTools, configOptionType } from './base-tools'; diff --git a/dist/lib/index.js b/dist/lib/index.js deleted file mode 100644 index bfaaf81..0000000 --- a/dist/lib/index.js +++ /dev/null @@ -1,6 +0,0 @@ -// src/lib/index.ts -// just group the export together -import createServer from './server'; -export { createServer }; -export { debugFn } from './helpers'; -export { BaseTools } from './base-tools'; diff --git a/dist/lib/option.d.ts b/dist/lib/option.d.ts deleted file mode 100644 index c5c5196..0000000 --- a/dist/lib/option.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare const defaultOptions: any; diff --git a/dist/lib/option.js b/dist/lib/option.js deleted file mode 100644 index 7aa8a07..0000000 --- a/dist/lib/option.js +++ /dev/null @@ -1,10 +0,0 @@ -// src/lib/option.ts -// base config option -// basically all the required options -export const defaultOptions = { - port: 8081, - provider: 'gitee', - path: '/webhook', - branch: 'refs/heads/master', - cmd: 'git pull origin master --no-edit' -}; diff --git a/dist/lib/server.d.ts b/dist/lib/server.d.ts deleted file mode 100644 index 248d21a..0000000 --- a/dist/lib/server.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * super simple http server using build-in node http - * @param {function} callback - * @param {object} config - * @param {function} debug - * @return {http.Server} - */ -declare function createBareServer(callback: any, config?: any, debug?: any): any; -export default createBareServer; diff --git a/dist/lib/server.js b/dist/lib/server.js deleted file mode 100644 index 765d899..0000000 --- a/dist/lib/server.js +++ /dev/null @@ -1,26 +0,0 @@ -// src/lib/server.ts -// create a barebone server -import { createServer } from 'http'; -/** - * super simple http server using build-in node http - * @param {function} callback - * @param {object} config - * @param {function} debug - * @return {http.Server} - */ -function createBareServer(callback, config = {}, debug = () => { }) { - const srv = createServer(callback); - if (process.env.NODE_ENV === 'test') { - return srv; - } - if (!config.port) { - throw new Error(`Expect a config.port property!`); - } - return srv.listen(config.port, () => { - try { - debug(`${config.provider} webhook server start @ ${config.port}`); - } - catch (e) { } - }); -} -export default createBareServer; diff --git a/dist/lib/types.d.ts b/dist/lib/types.d.ts deleted file mode 100644 index 8160b3f..0000000 --- a/dist/lib/types.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare type configOptionType = { - port: number; - dir?: string; - secret: string; - provider: string; - path: string; - branch: string; - cmd: string; - inited?: boolean; -}; -declare type resolvedPayloadType = { - header: any; - payload: any; -}; -export { configOptionType, resolvedPayloadType }; diff --git a/dist/lib/types.js b/dist/lib/types.js deleted file mode 100644 index 5ab2953..0000000 --- a/dist/lib/types.js +++ /dev/null @@ -1,3 +0,0 @@ -// src/lib/config-option-type -// just setup a type for the options to use -export {}; diff --git a/dist/main.d.ts b/dist/main.d.ts deleted file mode 100644 index fca5375..0000000 --- a/dist/main.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Finally the main method - * @param {object} config - * @return {function} for calls - */ -export declare function gitWebhookCi(options: any): any; diff --git a/dist/main.js b/dist/main.js deleted file mode 100644 index d749ae6..0000000 --- a/dist/main.js +++ /dev/null @@ -1,53 +0,0 @@ -// src/main.ts it was the main file so rename to main and the function rename as well -import { spawn } from 'child_process'; -import { getProvider } from './provider'; -import { defaultOptions } from './lib/option'; -import { debugFn } from './lib'; -const debug = debugFn('git-webhook-ci:main'); -const blackhole = debugFn('git-webhook-ci:blackhole:main'); -/** - * create a callback to execute - * @param {string} cmd - */ -function createCallback(cmd) { - // the signature just matching the cmd callback and create a problem here - return function callback(payload, opt) { - // don't really want to show this because it's pointless - blackhole(payload); - const ps = spawn(cmd[0], cmd.filter((_, i) => i > 0), opt); - ps.stdout.on('data', data => { - debug("cmd stdout:", data); - }); - ps.stderr.on('data', data => { - debug("cmd stderr:", data); - }); - ps.on('end', code => { - debug(`cmd exited with ${code}`); - }); - }; -} -/** - * Finally the main method - * @param {object} config - * @return {function} for calls - */ -export function gitWebhookCi(options) { - // yeah type safe ... you still need to do validation - if (typeof options !== 'object') { - throw new Error('Expecting options to be an object'); - } - const config = Object.assign({}, defaultOptions, options); - if (!config.secret || config.secret === '') { - throw new Error('You must provide the secret!'); - } - if (typeof config.cmd !== 'string' && typeof config.cmd !== 'function') { - throw new Error('Cmd must be a string or a function!'); - } - debug(config); - const createHandler = getProvider(config.provider); - // Return without Promise, because there is no need to - return createHandler(config, { - env: Object.assign({}, process.env), - cwd: config.dir ? config.dir : process.cwd() - }, typeof config.cmd === 'function' ? config.cmd : createCallback(config.cmd.split(' '))); -} diff --git a/dist/provider/gitee/gitee-handler.d.ts b/dist/provider/gitee/gitee-handler.d.ts deleted file mode 100644 index b72b399..0000000 --- a/dist/provider/gitee/gitee-handler.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { BaseTools, configOptionType } from '../../lib/base-tools'; -export declare class GiteeHandler extends BaseTools { - constructor(options: configOptionType); - /** - * Main method, the only one that get call - * @param {object} req the request - * @param {object} res the respond - * @param {function} callback res with 404 - * @return {null} nothing - */ - handler(req: any, res: any, callback: any): any; - /** - * Verify the password field - * @param {object} payload Content - * @return {object} promise - */ - private verify; - /** - * @param {object} res the respond - * @param {object} result the payload - * @return {null} nothing - */ - private resSuccess; -} diff --git a/dist/provider/gitee/gitee-handler.js b/dist/provider/gitee/gitee-handler.js deleted file mode 100644 index 8d9f764..0000000 --- a/dist/provider/gitee/gitee-handler.js +++ /dev/null @@ -1,74 +0,0 @@ -// src/provider/gitee/gitee-class.ts -import { BaseTools } from '../../lib/base-tools'; -import { verifyHandler } from './verify'; -import { debugFn } from '../../lib/helpers'; -const debug = debugFn('git-webhook-ci:gitee:handler'); -export class GiteeHandler extends BaseTools { - constructor(options) { - super(options); - } - /** - * Main method, the only one that get call - * @param {object} req the request - * @param {object} res the respond - * @param {function} callback res with 404 - * @return {null} nothing - */ - handler(req, res, callback) { - debug(`got call here`); - if (req.method !== 'POST' || req.url.split('?').shift() !== this.options.path) { - debug(req.url); - debug(this.options.path); - return callback(); // This is the only time we use the callback - } - this.parsePayload(req) - .then(obj => { - this.verify(obj) - .then(result => { - this.resSuccess(res, req, result); - }) - .catch((err) => { - this.resError(res, err); - }); - }); - } - /** - * Verify the password field - * @param {object} payload Content - * @return {object} promise - */ - verify(obj) { - return new Promise((resolver, rejecter) => { - const { header, payload } = obj; - if (verifyHandler(header, this.options.secret)) { - resolver(payload); - } - else { - rejecter(new Error('Gitee verify failed')); - } - }); - } - /** - * @param {object} res the respond - * @param {object} result the payload - * @return {null} nothing - */ - resSuccess(res, req, result) { - res.writeHead(200, { 'content-type': 'application/json' }); - res.end('{"ok":true}'); - // Check the result if this is what we wanted - if (result.hook_name === 'push_hooks') { // @TODO check if this is still correct - this.emit('push', { - payload: result, - host: req.headers.host, - event: result.hook_name - }); - } - else { - this.emit('error', { - msg: 'Not the event we are expecting', - event: result.hook_name - }); - } - } -} diff --git a/dist/provider/gitee/index.d.ts b/dist/provider/gitee/index.d.ts deleted file mode 100644 index 5872b47..0000000 --- a/dist/provider/gitee/index.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { configOptionType } from '../../lib'; -/** - * The main method to handle the server create and run the whole service for gitee - * @param {configOptionType} config for the overall setup of the system - * @param {object} opt this provide the environment variables to the cmd to execute later - * @param {function} callback - * @param {function} errorHandler optional - * @return {http server instance} - */ -declare function createGiteeServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; -export default createGiteeServer; diff --git a/dist/provider/gitee/index.js b/dist/provider/gitee/index.js deleted file mode 100644 index 1a0d2dd..0000000 --- a/dist/provider/gitee/index.js +++ /dev/null @@ -1,42 +0,0 @@ -// src/provider/gitee/index.ts -// the actual execution -import { GiteeHandler } from './gitee-handler'; -import { createServer, debugFn } from '../../lib'; -const debug = debugFn('git-webhook-ci:gitee'); -/** - * The main method to handle the server create and run the whole service for gitee - * @param {configOptionType} config for the overall setup of the system - * @param {object} opt this provide the environment variables to the cmd to execute later - * @param {function} callback - * @param {function} errorHandler optional - * @return {http server instance} - */ -function createGiteeServer(config, opt, callback, errorHandler = () => { }) { - const gitee = new GiteeHandler(config); - // just debug it out - gitee.on('error', (err) => { - debug('ERROR', err); - errorHandler(err); - }); - gitee.on('push', (result) => { - const ref = result.payload.ref; // ref is the branch name - if (config.branch === '*' || config.branch === ref) { - callback(result, opt, ref); - } - else { - errorHandler(ref); - debug('Gitee webhook is not expecting this branch', ref); - } - }); - // return the server instance - return createServer((req, res) => { - debug(`server callback executed`); - gitee.handler(req, res, (err) => { - debug('The url got called! [%s]', req.url, err); - errorHandler(req.url, err); - res.statusCode = 404; - res.end('-- no such location --'); - }); - }, config, debug); -} -export default createGiteeServer; diff --git a/dist/provider/gitee/verify.d.ts b/dist/provider/gitee/verify.d.ts deleted file mode 100644 index 659f63b..0000000 --- a/dist/provider/gitee/verify.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * create the secret key to compare - * @param {string} secretKey the secret key set during sestting up the webhook - * @param {number} timestamp this timestamp send from the git provider server - */ -export declare function getToken(secretKey: string, timestamp: number): string; -/** - * gitee has it's own payload structure therefore we need to check if we have those things in the header - * @param {*} header the parsed header from req - * @param {string} secretKey the secret key provided when setup the webhook - * @return {boolean} - */ -export declare function verifyHandler(header: any, secretKey: string): boolean; diff --git a/dist/provider/gitee/verify.js b/dist/provider/gitee/verify.js deleted file mode 100644 index 8ca1491..0000000 --- a/dist/provider/gitee/verify.js +++ /dev/null @@ -1,40 +0,0 @@ -// src/provider/gitee/secret.ts -// see here: https://gitee.com/help/articles/4290#article-header3 -import { createHmac } from 'crypto'; -// use the debug to find out what went wrong -import { debugFn } from '../../lib/helpers'; -const debug = debugFn('git-webhook-ci:gitee:verify'); -/** - * create the secret key to compare - * @param {string} secretKey the secret key set during sestting up the webhook - * @param {number} timestamp this timestamp send from the git provider server - */ -export function getToken(secretKey, timestamp) { - const secret_enc = Buffer.from(secretKey, 'utf8'); - const string_to_sign = `${timestamp}\n${secret_enc}`; - const hmac = createHmac('sha256', secret_enc); - const data = hmac.update(string_to_sign); - return encodeURIComponent(data.digest('base64')); -} -/** - * gitee has it's own payload structure therefore we need to check if we have those things in the header - * @param {*} header the parsed header from req - * @param {string} secretKey the secret key provided when setup the webhook - * @return {boolean} - */ -export function verifyHandler(header, secretKey) { - if (header['User-Agent'] === 'git-oschina-hook') { - debug('User-Agent passed', header['User-Agent']); - const expected = ['X-Gitee-Token', 'X-Gitee-Timestamp', 'X-Gitee-Event'].filter(key => header[key] !== undefined).length; - if (expected === 3) { - debug('Expected header passed'); - if (header['X-Gitee-Token'] === getToken(secretKey, parseInt(header['X-Gitee-Timestamp']))) { - return true; - } - else { - debug('verify the x-gitee-token failed'); - } - } - } - return false; -} diff --git a/dist/provider/github/index.d.ts b/dist/provider/github/index.d.ts deleted file mode 100644 index 8c5a2a9..0000000 --- a/dist/provider/github/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { configOptionType } from '../../lib'; -declare function createGithubServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; -export default createGithubServer; diff --git a/dist/provider/github/index.js b/dist/provider/github/index.js deleted file mode 100644 index 8014317..0000000 --- a/dist/provider/github/index.js +++ /dev/null @@ -1,37 +0,0 @@ -// src/provider/github/index.ts -// Github is using another external npm to handle it -import { createServer, debugFn } from '../../lib'; -import githubWebhook from 'github-webhook-handler'; -const debug = debugFn('git-webhook-ci:github'); -// main method -function createGithubServer(config, opt, callback, errorHandler = () => { }) { - const handler = githubWebhook({ - path: config.path, - secret: config.secret - }); - handler.on('error', err => { - errorHandler(err); - debug('Error:', err.message); - }); - // On received push event - handler.on('push', result => { - const ref = result.payload.ref; - if (config.branch === '*' || config.branch === ref) { - callback(result, opt, ref); - } - else { - const errorStr = `Received a push event for ${result.payload.repository.name} to ${ref}`; - debug(errorStr); - errorHandler(errorStr); - } - }); - return createServer((req, res) => { - handler(req, res, (err) => { - debug(`The url got called! ${req.url}`, err); - errorHandler(err); - res.statusCode = 404; - res.end('-- no such location --'); - }); - }, config, debug); -} -export default createGithubServer; diff --git a/dist/provider/gitlab/gitlab-handler.d.ts b/dist/provider/gitlab/gitlab-handler.d.ts deleted file mode 100644 index 36f5aa6..0000000 --- a/dist/provider/gitlab/gitlab-handler.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { BaseTools, configOptionType } from '../../lib/base-tools'; -export declare class GitlabHandler extends BaseTools { - constructor(options: configOptionType); - /** - * Main method - * @param {object} req the request - * @param {object} res the respond - * @param {function} callback res with 404 - * @param {function} errorHandler optional error callback - * @return {null} nothing - */ - handler(req: any, res: any, callback: any): any; - /** - * Verify the password field - * @param {object} payload Content - * @param {object} headers headers looking for the X-Gitlab-Event: Push Hook - * @return {object} promise - */ - private verify; - /** - * @param {object} res the respond - * @param {object} result the payload - * @return {null} nothing - */ - private resSuccess; -} diff --git a/dist/provider/gitlab/gitlab-handler.js b/dist/provider/gitlab/gitlab-handler.js deleted file mode 100644 index f546d68..0000000 --- a/dist/provider/gitlab/gitlab-handler.js +++ /dev/null @@ -1,73 +0,0 @@ -// src/provider/gitlab/gitlab-handler.ts -import { BaseTools } from '../../lib/base-tools'; -export class GitlabHandler extends BaseTools { - constructor(options) { - super(options); - } - /** - * Main method - * @param {object} req the request - * @param {object} res the respond - * @param {function} callback res with 404 - * @param {function} errorHandler optional error callback - * @return {null} nothing - */ - handler(req, res, callback) { - if (req.url.split('?').shift() !== this.options.path || req.method !== 'POST') { - return callback(); // The only time we use the callback - } - this.parsePayload(req).then(payload => { - const headers = req.headers; // JSON.stringify(req.headers, replacer); - // cache = null; - this.verify(payload, headers) - .then(payload => { - this.resSuccess(res, req, payload); - }) - .catch((err) => { - this.resError(res, err); - }); - }); - } - /** - * Verify the password field - * @param {object} payload Content - * @param {object} headers headers looking for the X-Gitlab-Event: Push Hook - * @return {object} promise - */ - verify(payload, headers) { - const eventName = 'X-Gitlab-Event'.toLowerCase(); - const token = 'X-Gitlab-Token'.toLowerCase(); - // Console.log('headers', headers, typeof headers); - return new Promise((resolver, rejecter) => { - if (headers[eventName] === 'Push Hook' && headers[token] === this.options.secret) { - resolver(payload); - } - else { - rejecter(new Error('Verify failed')); - } - }); - } - /** - * @param {object} res the respond - * @param {object} result the payload - * @return {null} nothing - */ - resSuccess(res, req, payload) { - res.writeHead(200, { 'content-type': 'application/json' }); - res.end('{"ok":true}'); - // Check the result if this is what we wanted - if (payload.object_kind === 'push') { - this.emit('push', { - payload: payload, - host: req.headers.host, - event: payload.object_kind - }); - } - else { - this.emit('error', { - msg: 'Not the event we are expecting', - event: payload.object_kind - }); - } - } -} diff --git a/dist/provider/gitlab/index.d.ts b/dist/provider/gitlab/index.d.ts deleted file mode 100644 index a708d6c..0000000 --- a/dist/provider/gitlab/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { configOptionType } from '../../lib'; -declare function createGitlabServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; -export default createGitlabServer; diff --git a/dist/provider/gitlab/index.js b/dist/provider/gitlab/index.js deleted file mode 100644 index 6724c16..0000000 --- a/dist/provider/gitlab/index.js +++ /dev/null @@ -1,33 +0,0 @@ -// src/provider/gitlab/index.ts -import { GitlabHandler } from './gitlab-handler'; -import { createServer, debugFn } from '../../lib'; -const debug = debugFn('git-webhook-ci:gitlab'); -// main method -function createGitlabServer(config, opt, callback, errorHandler = () => { }) { - const gitlab = new GitlabHandler(config); - gitlab.on('error', (err) => { - debug('error', err); - errorHandler(err); - }); - // Listen on the push event - success - gitlab.on('push', (result) => { - const ref = result.payload.ref; - if (config.branch === '*' || config.branch === ref) { - callback(result, opt, ref); - } - else { - errorHandler(ref); - debug('Gitee webhook is not expecting this branch', ref); - } - }); - // return the http server - return createServer((req, res) => { - gitlab.handler(req, res, (err) => { - debug('The url got called! [%s]', req.url, err); - errorHandler(err); - res.statusCode = 404; - res.end('-- no such location --'); - }); - }, config, debug); -} -export default createGitlabServer; diff --git a/dist/provider/index.d.ts b/dist/provider/index.d.ts deleted file mode 100644 index 9d40430..0000000 --- a/dist/provider/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Wrap all the configuration check code here - * Then init the instance and return it - * @NOTE v.2 gitee now is the default - * also it's now a named export - */ -export declare function getProvider(provider: string): any; diff --git a/dist/provider/index.js b/dist/provider/index.js deleted file mode 100644 index eb576a7..0000000 --- a/dist/provider/index.js +++ /dev/null @@ -1,25 +0,0 @@ -// src/provider/index.ts -// group everything together and call from here -import giteeWebhook from './gitee'; -import githubWebhook from './github'; -import gitlabWebhook from './gitlab'; -import wechatWebhook from './wechat'; -/** - * Wrap all the configuration check code here - * Then init the instance and return it - * @NOTE v.2 gitee now is the default - * also it's now a named export - */ -export function getProvider(provider) { - switch (provider) { - case 'wechat': - return wechatWebhook; - case 'gitlab': - return gitlabWebhook; - case 'github': - return githubWebhook; - case 'gitee': - default: - return giteeWebhook; - } -} diff --git a/dist/provider/wechat/index.d.ts b/dist/provider/wechat/index.d.ts deleted file mode 100644 index c37373c..0000000 --- a/dist/provider/wechat/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { configOptionType } from '../../lib'; -declare function createWechatServer(config: configOptionType, opt: any, callback: any, errorHandler?: any): any; -export default createWechatServer; diff --git a/dist/provider/wechat/index.js b/dist/provider/wechat/index.js deleted file mode 100644 index e3dcbe3..0000000 --- a/dist/provider/wechat/index.js +++ /dev/null @@ -1,24 +0,0 @@ -// src/provider/wechat/index.ts -import { WechatHandler } from './wechat-handler'; -import { createServer, debugFn } from '../../lib'; -const debug = debugFn('git-webhook-ci:wechat'); -function createWechatServer(config, opt, callback, errorHandler = () => { }) { - const wechat = new WechatHandler(config); - /* This is not implemented, there is no need at the moment - wechat.on('error', err => { - debug('error', err); - }); - */ - wechat.on('push', result => { - callback(result, opt); - }); - return createServer((req, res) => { - wechat.handler(req, res, (err) => { - errorHandler(err); - debug('The url got called! [%s]', req.url, err); - res.statusCode = 404; - res.end('-- no such location --'); - }); - }, config, debug); -} -export default createWechatServer; diff --git a/dist/provider/wechat/wechat-handler.d.ts b/dist/provider/wechat/wechat-handler.d.ts deleted file mode 100644 index 40d2049..0000000 --- a/dist/provider/wechat/wechat-handler.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * This is for wechat mini-app push callback - * Based on their PHP version - */ -import { BaseTools, configOptionType } from '../../lib'; -export declare class WechatHandler extends BaseTools { - constructor(options: configOptionType); - /** - * Main interface, this is different from the other because there is no filter - * on what is coming, just verify it then pass the payload to the callback - */ - handler(req: any, res: any, callback: any): void; - private getParams; - /** - * This is different using the query parameter to compare - * Another thing is - this is a one off verify process - * once the wechat end verify this end is correct, it will - * just send data over. Need to figure out a way to run this - * verify before the actual listening - */ - private verify; -} diff --git a/dist/provider/wechat/wechat-handler.js b/dist/provider/wechat/wechat-handler.js deleted file mode 100644 index 3b06904..0000000 --- a/dist/provider/wechat/wechat-handler.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * This is for wechat mini-app push callback - * Based on their PHP version - */ -/* -https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/callback_help.html - -private function checkSignature() -{ - $signature = $_GET["signature"]; - $timestamp = $_GET["timestamp"]; - $nonce = $_GET["nonce"]; - - $token = TOKEN; - $tmpArr = array($token, $timestamp, $nonce); - sort($tmpArr, SORT_STRING); - $tmpStr = implode( $tmpArr ); - $tmpStr = sha1( $tmpStr ); - - if( $tmpStr == $signature ){ - return true; - }else{ - return false; - } -} -*/ -import { BaseTools, debugFn } from '../../lib'; -import { URL } from 'url'; -import sha1 from 'sha1'; -const debug = debugFn('git-webhook-ci:wechat'); -export class WechatHandler extends BaseTools { - constructor(options) { - super(options); - } - /** - * Main interface, this is different from the other because there is no filter - * on what is coming, just verify it then pass the payload to the callback - */ - handler(req, res, callback) { - if (this.options.inited !== true) { - const echostr = this.verify(req); - if (!echostr) { - debug('verify with wechat server failed'); - return callback('verify failed'); - } - debug(`verify with wechat echostr '${echostr}' correct`); - res.writeHead(200, { 'content-type': 'text/html' }); - res.end(echostr); - return; - } - // The implementation is different - this.parsePayload(req) - .then(payload => { - res.writeHead(200, { 'content-type': 'application/json' }); - res.end('{"ok": true}'); - // Just reuse the same naming - this.emit('push', { - payload, - host: req.headers.host, - event: 'wechat-push' - }); - }); - } - // get the params from url - getParams(url) { - const u = new URL(url); - const params = ['signature', 'timestamp', 'nonce', 'echostr']; - return params.map((param) => { - return { [param]: u.searchParams.get(param) }; - }).reduce((a, b) => Object.assign(a, b), {}); - } - /** - * This is different using the query parameter to compare - * Another thing is - this is a one off verify process - * once the wechat end verify this end is correct, it will - * just send data over. Need to figure out a way to run this - * verify before the actual listening - */ - verify(req) { - const { signature, timestamp, nonce, echostr } = this.getParams(req.url); - const $token = this.options.secret; - let $tmpArr = [$token, timestamp, nonce]; - $tmpArr.sort(); - return sha1($tmpArr.join('')) === signature ? echostr : false; - } -} diff --git a/src/cli.ts b/docs/02-cli.md similarity index 85% rename from src/cli.ts rename to docs/02-cli.md index 34e76e6..46a870f 100644 --- a/src/cli.ts +++ b/docs/02-cli.md @@ -1,14 +1,10 @@ -#!/usr/bin/env node -/** - * Run from cli - * dir: '', - * path: '/webhook', - * port: 8081, - * branch: 'refs/heads/master', - * cmd: 'git pull origin master --no-edit' - */ -// import { configOptionType } from './lib' -import { gitWebhookCi } from './main' +# Write your own CLI + +Here we show you the original CLI code in the V.1 (update to using Typescript) + + +```js +import gitWebhookCi from 'git-webhook-ci' import meow from 'meow' const helpText: string = ` @@ -81,3 +77,6 @@ const serve = function(p: string, flags: any): any { } // Run it serve(cli.input[0], cli.flags) + + +``` diff --git a/src/lib/option.ts b/src/lib/option.ts index 9852e84..0e375e0 100644 --- a/src/lib/option.ts +++ b/src/lib/option.ts @@ -1,6 +1,9 @@ // src/lib/option.ts // base config option +// @NOTE here is a problem github use /payload as default now + + // basically all the required options export const defaultOptions: any = { port: 8081, -- Gitee From ac8e4aefb28f6b80139c264c6fa909bc44455073 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Sat, 19 Jun 2021 14:31:46 +0800 Subject: [PATCH 14/21] start taking down the old github code --- package.json | 3 ++- src/lib/base-tools.ts | 7 ++++++ src/lib/option.ts | 5 ++-- src/lib/types.ts | 6 +++-- src/provider/gitee/gitee-handler.ts | 2 +- src/provider/github/github-handler.ts | 17 ++++++++++++++ src/provider/github/verify.ts | 34 +++++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/provider/github/github-handler.ts create mode 100644 src/provider/github/verify.ts diff --git a/package.json b/package.json index 43e4c03..7ab490f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "init": "tsc --init", "build": "tsc -p tsconfig.json", "clean": "node ./clean.js", - "ts-node": "ts-node" + "ts-node": "ts-node", + "" }, "keywords": [ "git", diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index 579a9cb..6f44768 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -46,7 +46,9 @@ class BaseTools extends EventEmitter { .on('end', () => { // should catch error here as well try { + const json: string = Buffer.concat(body).toString() + resolver({ header, payload: JSON.parse(json) @@ -73,6 +75,11 @@ class BaseTools extends EventEmitter { ) this.emit('error', new Error(err)) } + + // simple clean up method to get the url path + getUrlPath(req: any): string { + return req.url.split('?').shift() + } } export { BaseTools, configOptionType } diff --git a/src/lib/option.ts b/src/lib/option.ts index 0e375e0..bdd3302 100644 --- a/src/lib/option.ts +++ b/src/lib/option.ts @@ -1,7 +1,7 @@ // src/lib/option.ts // base config option -// @NOTE here is a problem github use /payload as default now +// @NOTE here is a problem github use /payload as default now // basically all the required options @@ -10,5 +10,6 @@ export const defaultOptions: any = { provider: 'gitee', path: '/webhook', branch: 'refs/heads/master', - cmd: 'git pull origin master --no-edit' + cmd: 'git pull origin master --no-edit', + error: () => {} // just a placeholder } diff --git a/src/lib/types.ts b/src/lib/types.ts index 4b67f59..a117eff 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -3,13 +3,15 @@ type configOptionType = { port: number, - dir?: string, secret: string, provider: string, path: string, branch: string, cmd: string, - inited?: boolean + // @TODO should we put the cwd and env here as well? + error?: any, + inited?: boolean, + dir?: string } type resolvedPayloadType = { diff --git a/src/provider/gitee/gitee-handler.ts b/src/provider/gitee/gitee-handler.ts index 44c380b..47c86f1 100644 --- a/src/provider/gitee/gitee-handler.ts +++ b/src/provider/gitee/gitee-handler.ts @@ -21,7 +21,7 @@ export class GiteeHandler extends BaseTools { */ public handler(req: any, res: any, callback: any): any { debug(`got call here`) - if (req.method !== 'POST' || req.url.split('?').shift() !== this.options.path) { + if (req.method !== 'POST' || this.getUrlPath(req) !== this.options.path) { debug(req.url) debug(this.options.path) return callback() // This is the only time we use the callback diff --git a/src/provider/github/github-handler.ts b/src/provider/github/github-handler.ts new file mode 100644 index 0000000..552c9dd --- /dev/null +++ b/src/provider/github/github-handler.ts @@ -0,0 +1,17 @@ +// src/provider/github/github-handler.ts +// V.2 ditch the external github-webhook-handler +import { BaseTools, configOptionType } from '../../lib/base-tools' +import { verifyHandler } from './verify' +import { debugFn } from '../../lib/helpers' + +const debug = debugFn('git-webhook-ci:github:handler') + +export class GiteeHandler extends BaseTools { + + constructor(options: configOptionType) { + super(options) + } + + + +} diff --git a/src/provider/github/verify.ts b/src/provider/github/verify.ts new file mode 100644 index 0000000..a014e47 --- /dev/null +++ b/src/provider/github/verify.ts @@ -0,0 +1,34 @@ +// src/provider/github/verify.ts + + + +import { createHmac } from 'crypto' + +/** + * Github now change the way how to validate the signature + * We need to implement it here, and it's using the payload to hash + * the whole key starts with `sha256=` using a HMAC hex + */ +export function getToken(secretKey: string, payload: any): string { + const hmac = createHmac('sha256', secretKey) + + hmac.update(payload) // note here, you must make sure this is utf-8 encoded before passing here + + return 'sha256=' + hmac.digest('hex') // return the hex format +} + +/** + * This is the actual call inside the verify method in the class + * @param {object} header the raw header + * @param {string} secretKey + * @param {object} payload the full payload encoded in utf8 + * @return {boolean} + */ +export function verifyHandler(header: any, secretKey: string, payload: any): boolean { + if (header['X-Hub-Signature-256']) { + + return header['X-Hub-Signature-256'] === getToken(secretKey, payload) + } + + return false +} -- Gitee From 17c4d0a99ee7f7fe60a1c8c6edc263afc289bf24 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Tue, 22 Jun 2021 09:55:01 +0800 Subject: [PATCH 15/21] update the files in package.json --- package.json | 6 ++++++ ts-run.js => run.js | 0 2 files changed, 6 insertions(+) rename ts-run.js => run.js (100%) diff --git a/package.json b/package.json index 7ab490f..96a67bd 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,12 @@ "version": "2.0.0-alpha.1", "description": "Using git provider webhook to create a poorman CI system ", "main": "dist/index.js", + "files": [ + "docs", + "src", + "clean.js", + "run.js" + ], "scripts": { "test": "ava", "test:p": "ava --verbose ./tests/partial.test.ts", diff --git a/ts-run.js b/run.js similarity index 100% rename from ts-run.js rename to run.js -- Gitee From 1f0959f439ad518da95539daccaf7efd7bb4e573 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Tue, 22 Jun 2021 20:11:52 +0800 Subject: [PATCH 16/21] update the base class and the two children --- package.json | 3 +-- src/lib/base-tools.ts | 25 ++++++++++++++++- src/provider/gitee/gitee-handler.ts | 23 ++++++---------- src/provider/github/github-handler.ts | 39 +++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 96a67bd..d315f40 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,7 @@ "init": "tsc --init", "build": "tsc -p tsconfig.json", "clean": "node ./clean.js", - "ts-node": "ts-node", - "" + "ts-node": "" }, "keywords": [ "git", diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index 6f44768..5fd0147 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -2,7 +2,9 @@ import EventEmitter from 'events' import { configOptionType, resolvedPayloadType } from './types' +import { debugFn } from './helpers' +const debug = debugFn('git-webhook-ci:base-tools') class BaseTools extends EventEmitter { @@ -77,9 +79,30 @@ class BaseTools extends EventEmitter { } // simple clean up method to get the url path - getUrlPath(req: any): string { + protected getUrlPath(req: any): string { return req.url.split('?').shift() } + + // put the validate method here + protected validate(req: any): boolean { + return req.method !== 'POST' || this.getUrlPath(req) !== this.options.path + } + + // this will be overwritten by the child + protected handler(req: any, res: any, verifyFn: any): Promise { + if (!this.validate(req)) { + debug(req.url, this.options.path) + return Promise.reject(new Error(`Path validate failed`)) + } + return this.parsePayload(req) + .then(obj => { + return verifyFn(obj) + .catch((err: any) => { + this.resError(res, err) + }) + }) + } + } export { BaseTools, configOptionType } diff --git a/src/provider/gitee/gitee-handler.ts b/src/provider/gitee/gitee-handler.ts index 47c86f1..a2dcb8f 100644 --- a/src/provider/gitee/gitee-handler.ts +++ b/src/provider/gitee/gitee-handler.ts @@ -21,20 +21,12 @@ export class GiteeHandler extends BaseTools { */ public handler(req: any, res: any, callback: any): any { debug(`got call here`) - if (req.method !== 'POST' || this.getUrlPath(req) !== this.options.path) { - debug(req.url) - debug(this.options.path) - return callback() // This is the only time we use the callback - } - this.parsePayload(req) - .then(obj => { - this.verify(obj) - .then(result => { - this.resSuccess(res, req, result) - }) - .catch((err: any) => { - this.resError(res, err) - }) + return super.handler(req, res, this.verify) + .then(result => { + this.resSuccess(req, res, result) + }) + .catch(err => { + return callback(err) }) } @@ -55,11 +47,12 @@ export class GiteeHandler extends BaseTools { } /** + * @param {object} req the request * @param {object} res the respond * @param {object} result the payload * @return {null} nothing */ - private resSuccess(res: any, req: any, result: any) { + private resSuccess(req: any, res: any, result: any) { res.writeHead(200, { 'content-type': 'application/json' }) res.end('{"ok":true}') // Check the result if this is what we wanted diff --git a/src/provider/github/github-handler.ts b/src/provider/github/github-handler.ts index 552c9dd..1563ae0 100644 --- a/src/provider/github/github-handler.ts +++ b/src/provider/github/github-handler.ts @@ -12,6 +12,45 @@ export class GiteeHandler extends BaseTools { super(options) } + // How to make this into the parent method + public handler(req: any, res: any, callback: any): any { + debug(`github handler called`) + return super.handler(req, res, this.verify) + .then(result => { + this.resSuccess(req, res, result) + }) + .catch(err => { + return callback(err) + }) + } + + private verify(obj: any): Promise { + return new Promise((resolver: any, rejecter: any): void => { + const { header, payload } = obj + if (verifyHandler(header, this.options.secret, payload)) { + resolver(payload) + } else { + rejecter(new Error('Github verify failed')) + } + }) + } + private resSuccess(req: any, res: any, result: any) { + res.writeHead(200, { 'content-type': 'application/json' }) + // this might be different take a look at the github module + res.end('{"ok":true}') + if (result.hook_name === 'push_hooks') { // @TODO check if this is still correct + this.emit('push', { + payload: result, + host: req.headers.host, + event: result.hook_name + }) + } else { + this.emit('error', { + msg: 'Not the event we are expecting', + event: result.hook_name + }) + } + } } -- Gitee From 7eab3362630221abf3798cd7d7addb60c06eed55 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Tue, 22 Jun 2021 21:04:26 +0800 Subject: [PATCH 17/21] Gitee refactor complete and tested --- src/lib/base-tools.ts | 9 ++++++--- src/provider/gitee/gitee-handler.ts | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib/base-tools.ts b/src/lib/base-tools.ts index 5fd0147..8b38604 100644 --- a/src/lib/base-tools.ts +++ b/src/lib/base-tools.ts @@ -35,6 +35,7 @@ class BaseTools extends EventEmitter { * @return {object} Promise */ protected parsePayload(req: any): Promise { + debug('call parsePayload') return new Promise((resolver: any, rejecter: any): void => { // V.2 here we also need to parse the header and add to the json // and the result object will become { payload: Object, header: Object } @@ -85,18 +86,20 @@ class BaseTools extends EventEmitter { // put the validate method here protected validate(req: any): boolean { - return req.method !== 'POST' || this.getUrlPath(req) !== this.options.path + return req.method === 'POST' && this.getUrlPath(req) === this.options.path } - + // this will be overwritten by the child protected handler(req: any, res: any, verifyFn: any): Promise { if (!this.validate(req)) { debug(req.url, this.options.path) return Promise.reject(new Error(`Path validate failed`)) } + return this.parsePayload(req) .then(obj => { - return verifyFn(obj) + + return verifyFn(obj, this.options) // we move the this.options as param to get round the scope problem .catch((err: any) => { this.resError(res, err) }) diff --git a/src/provider/gitee/gitee-handler.ts b/src/provider/gitee/gitee-handler.ts index a2dcb8f..8ee7732 100644 --- a/src/provider/gitee/gitee-handler.ts +++ b/src/provider/gitee/gitee-handler.ts @@ -35,10 +35,10 @@ export class GiteeHandler extends BaseTools { * @param {object} payload Content * @return {object} promise */ - private verify(obj: any): Promise { + private verify(obj: any, opt: any): Promise { return new Promise((resolver: any, rejecter: any): void => { const { header, payload } = obj - if (verifyHandler(header, this.options.secret)) { + if (verifyHandler(header, opt.secret)) { resolver(payload) } else { rejecter(new Error('Gitee verify failed')) -- Gitee From 6419c123d3a5262781166994e97fd4bb0c98153f Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Tue, 22 Jun 2021 21:07:34 +0800 Subject: [PATCH 18/21] refactor the github verify method --- src/provider/github/github-handler.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/provider/github/github-handler.ts b/src/provider/github/github-handler.ts index 1563ae0..3f0a384 100644 --- a/src/provider/github/github-handler.ts +++ b/src/provider/github/github-handler.ts @@ -24,10 +24,11 @@ export class GiteeHandler extends BaseTools { }) } - private verify(obj: any): Promise { + // github token verify method + private verify(obj: any, opt: any): Promise { return new Promise((resolver: any, rejecter: any): void => { const { header, payload } = obj - if (verifyHandler(header, this.options.secret, payload)) { + if (verifyHandler(header, opt.secret, payload)) { resolver(payload) } else { rejecter(new Error('Github verify failed')) -- Gitee From 826cb0fe5c3f51122d8678dd76db7dbd0c265e75 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Wed, 23 Jun 2021 09:27:24 +0800 Subject: [PATCH 19/21] refactor the create handler and get rip of the opt, it has merge into the config --- run.js | 2 ++ src/lib/option.ts | 2 ++ src/lib/types.ts | 9 +++++---- src/main.ts | 6 ++---- src/provider/gitee/index.ts | 5 ++--- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/run.js b/run.js index 6c81b79..7edab48 100644 --- a/run.js +++ b/run.js @@ -1,3 +1,5 @@ // ts-run.js try to use esbuild-register to run this without the compilation // @TODO + +// Here we just run a github instance for testing purpose diff --git a/src/lib/option.ts b/src/lib/option.ts index bdd3302..45152ed 100644 --- a/src/lib/option.ts +++ b/src/lib/option.ts @@ -11,5 +11,7 @@ export const defaultOptions: any = { path: '/webhook', branch: 'refs/heads/master', cmd: 'git pull origin master --no-edit', + pwd: process.cwd(), + env: process.env, error: () => {} // just a placeholder } diff --git a/src/lib/types.ts b/src/lib/types.ts index a117eff..ccc89c4 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -7,11 +7,12 @@ type configOptionType = { provider: string, path: string, branch: string, - cmd: string, - // @TODO should we put the cwd and env here as well? + cmd: string | any, + // @TODO + pwd?: string, + env?: any, error?: any, - inited?: boolean, - dir?: string + inited?: boolean } type resolvedPayloadType = { diff --git a/src/main.ts b/src/main.ts index 4fee6f1..2efbbbc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,7 @@ const debug = debugFn('git-webhook-ci:main') /** * create a callback to execute + * @param {object} opt --> need this to pass the env and pwd to spawn * @param {string} cmd */ function createCallback(cmd: Array): any { @@ -42,6 +43,7 @@ export function gitWebhookCi(options: any): any { } const config = Object.assign({}, defaultOptions, options) + if (!config.secret || config.secret === '') { throw new Error('You must provide the secret!') } @@ -56,10 +58,6 @@ export function gitWebhookCi(options: any): any { // Return without Promise, because there is no need to return createHandler( config, - { - env: Object.assign({}, process.env), - cwd: config.dir ? config.dir : process.cwd() - }, typeof config.cmd === 'function' ? config.cmd : createCallback(config.cmd.split(' ')) ) } diff --git a/src/provider/gitee/index.ts b/src/provider/gitee/index.ts index 5ef30e9..5434792 100644 --- a/src/provider/gitee/index.ts +++ b/src/provider/gitee/index.ts @@ -10,12 +10,11 @@ const debug = debugFn('git-webhook-ci:gitee') /** * The main method to handle the server create and run the whole service for gitee * @param {configOptionType} config for the overall setup of the system - * @param {object} opt this provide the environment variables to the cmd to execute later * @param {function} callback * @param {function} errorHandler optional * @return {http server instance} */ -function createGiteeServer(config: configOptionType, opt: any, callback: any, errorHandler: any = () => {}): any { +function createGiteeServer(config: configOptionType, callback: any, errorHandler: any = () => {}): any { const gitee: GiteeHandler = new GiteeHandler(config) // just debug it out @@ -27,7 +26,7 @@ function createGiteeServer(config: configOptionType, opt: any, callback: any, er gitee.on('push', (result: any) => { const ref = result.payload.ref // ref is the branch name if (config.branch === '*' || config.branch === ref) { - callback(result, opt, ref) + callback(result, config, ref) } else { errorHandler(ref) debug('Gitee webhook is not expecting this branch', ref) -- Gitee From 0e1b665ccd5e2b1e60b428498e1576232eea06c5 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Wed, 23 Jun 2021 10:06:15 +0800 Subject: [PATCH 20/21] gitee handler refactor completed --- src/main.ts | 3 ++- src/provider/github/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main.ts b/src/main.ts index 2efbbbc..b99963b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -58,6 +58,7 @@ export function gitWebhookCi(options: any): any { // Return without Promise, because there is no need to return createHandler( config, - typeof config.cmd === 'function' ? config.cmd : createCallback(config.cmd.split(' ')) + typeof config.cmd === 'function' ? config.cmd : createCallback(config.cmd.split(' ')), + config.error // this is the error Handler ) } diff --git a/src/provider/github/index.ts b/src/provider/github/index.ts index 7355fac..a81f36f 100644 --- a/src/provider/github/index.ts +++ b/src/provider/github/index.ts @@ -8,7 +8,7 @@ import githubWebhook from 'github-webhook-handler' const debug = debugFn('git-webhook-ci:github') // main method -function createGithubServer(config: configOptionType, opt: any, callback: any, errorHandler: any = () => {}): any { +function createGithubServer(config: configOptionType, callback: any, errorHandler: any = () => {}): any { const handler = githubWebhook({ path: config.path, secret: config.secret @@ -22,7 +22,7 @@ function createGithubServer(config: configOptionType, opt: any, callback: any, e handler.on('push', result => { const ref = result.payload.ref if (config.branch === '*' || config.branch === ref) { - callback(result, opt, ref) + callback(result, config, ref) } else { const errorStr = `Received a push event for ${result.payload.repository.name} to ${ref}` debug(errorStr) -- Gitee From 864b7d83953d374c3e5b1059bcd2aed274ccbee5 Mon Sep 17 00:00:00 2001 From: Joel Chu Date: Wed, 23 Jun 2021 13:05:21 +0800 Subject: [PATCH 21/21] sort out the ts run param --- .gitignore | 1 + package.json | 3 ++- run.js => run.ts | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename run.js => run.ts (100%) diff --git a/.gitignore b/.gitignore index 82c6a74..d396102 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules tests/fixtures/py/* +hello.ts diff --git a/package.json b/package.json index d315f40..50c6810 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,12 @@ "test": "ava", "test:p": "ava --verbose ./tests/partial.test.ts", "test:gitee": "DEBUG=git-webhook-ci:* ava --verbose ./tests/gitee.test.ts", + "test:run": "npm run ts -- run.ts", "lint": "eslint src/ --ext .js,.jsx,.ts,.tsx", "init": "tsc --init", "build": "tsc -p tsconfig.json", "clean": "node ./clean.js", - "ts-node": "" + "ts": "node -r esbuild-register" }, "keywords": [ "git", diff --git a/run.js b/run.ts similarity index 100% rename from run.js rename to run.ts -- Gitee