From beb2cdcf590c2c2c1677a73b9642db27140c7077 Mon Sep 17 00:00:00 2001 From: Dan Hellem Date: Wed, 16 Feb 2022 07:27:13 -0800 Subject: [PATCH] refactor, md to html, formatting --- package-lock.json | 146 +++++++++++++++++++++------- package.json | 8 +- src/debug/sample.webhookpayload.ts | 20 ++-- src/github-pr.ts | 15 +-- src/main.ts | 111 +++++++--------------- src/patch-documents.ts | 99 ++++++++----------- src/workitems.ts | 148 ++++++++++------------------- 7 files changed, 256 insertions(+), 291 deletions(-) diff --git a/package-lock.json b/package-lock.json index e507cb2..cb3ecd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -696,6 +696,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==" }, + "@types/showdown": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-1.9.4.tgz", + "integrity": "sha512-50ehC3IAijfkvoNqmQ+VL73S7orOxmAK8ljQAFBv8o7G66lAZyxQj1L3BAv2dD86myLXI+sgKP1kcxAaxW356w==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -876,14 +882,12 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -1342,8 +1346,7 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "capture-exit": { "version": "2.0.0", @@ -1425,7 +1428,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, "requires": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", @@ -1436,7 +1438,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -1447,7 +1448,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -1474,7 +1474,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -1482,8 +1481,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "combined-stream": { "version": "1.0.8", @@ -1640,8 +1638,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decode-uri-component": { "version": "0.2.0", @@ -1759,8 +1756,7 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "end-of-stream": { "version": "1.4.4", @@ -2913,8 +2909,7 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-stdin": { "version": "6.0.0", @@ -3387,8 +3382,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-generator-fn": { "version": "2.1.0", @@ -4819,8 +4813,7 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", @@ -5215,14 +5208,12 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "resolve": { "version": "1.13.1", @@ -5364,8 +5355,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { "version": "2.0.1", @@ -5409,6 +5399,99 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, + "showdown": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz", + "integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==", + "requires": { + "yargs": "^14.2" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.3.tgz", + "integrity": "sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -6250,8 +6333,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "windows-release": { "version": "3.3.0", @@ -6271,7 +6353,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, "requires": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -6282,7 +6363,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -6293,7 +6373,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -6343,8 +6422,7 @@ "y18n": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" }, "yargs": { "version": "13.3.0", diff --git a/package.json b/package.json index fb16e21..e968175 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,11 @@ "description": "Create a work item on an Azure Board when a GitHub Pull Request is created", "main": "lib/main.js", "scripts": { - "build": "tsc", + "build": "tsc", "lint": "eslint src/**/*.ts", "pack": "ncc build", "test": "jest", - "all": "npm run build && npm run lint && npm run pack && npm test" + "all": "npm run build && npm run pack && npm test" }, "repository": { "type": "git", @@ -25,11 +25,13 @@ "dependencies": { "@actions/core": "^1.2.6", "@actions/github": "^2.1.1", - "azure-devops-node-api": "^10.1.0" + "azure-devops-node-api": "^10.1.0", + "showdown": "^1.9.1" }, "devDependencies": { "@types/jest": "^24.0.23", "@types/node": "^12.7.12", + "@types/showdown": "^1.9.4", "@typescript-eslint/parser": "^2.8.0", "@zeit/ncc": "^0.20.5", "eslint": "^6.8.0", diff --git a/src/debug/sample.webhookpayload.ts b/src/debug/sample.webhookpayload.ts index 7cfbb61..ede98b6 100644 --- a/src/debug/sample.webhookpayload.ts +++ b/src/debug/sample.webhookpayload.ts @@ -2,16 +2,16 @@ import {WebhookPayload} from '@actions/github/lib/interfaces' const sampleWebHookPayload: WebhookPayload = { action: 'opened', - number: 10, + number: 14, pull_request: { - url: 'https://api.github.com/repos/danhellem/myapp-web/pulls/10', + url: 'https://api.github.com/repos/danhellem/myapp-web/pulls/14', id: 847975880, node_id: 'MDExOlB1bGxSZXF1ZXN0NDE0MjA1NTg1', - html_url: 'https://github.com/danhellem/Lorem-ipsum/pull/10', - diff_url: 'https://github.com/danhellem/Lorem-ipsum/pull/10.diff', - patch_url: 'https://github.com/danhellem/Lorem-ipsum/pull/10.patch', - issue_url: 'https://api.github.com/repos/danhellem/myapp-web/issues/10', - number: 10, + html_url: 'https://github.com/danhellem/Lorem-ipsum/pull/14', + diff_url: 'https://github.com/danhellem/Lorem-ipsum/pull/14.diff', + patch_url: 'https://github.com/danhellem/Lorem-ipsum/pull/14.patch', + issue_url: 'https://api.github.com/repos/danhellem/myapp-web/issues/14', + number: 14, state: 'open', locked: false, title: 'Update README.md', @@ -120,7 +120,7 @@ const sampleWebHookPayload: WebhookPayload = { site_admin: true }, html_url: 'https://github.com/danhellem/Lorem-ipsum', - description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', + description: null, fork: false, url: 'https://api.github.com/repos/danhellem/Lorem-ipsum', forks_url: 'https://api.github.com/repos/danhellem/Lorem-ipsum/forks', @@ -283,7 +283,7 @@ const sampleWebHookPayload: WebhookPayload = { site_admin: true }, html_url: 'https://github.com/danhellem/Lorem-ipsum', - description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', + description: null, fork: false, url: 'https://api.github.com/repos/danhellem/Lorem-ipsum', forks_url: 'https://api.github.com/repos/danhellem/Lorem-ipsum/forks', @@ -462,7 +462,7 @@ const sampleWebHookPayload: WebhookPayload = { site_admin: true }, html_url: 'https://github.com/danhellem/Lorem-ipsum', - description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', + description: null, fork: false, url: 'https://api.github.com/repos/danhellem/Lorem-ipsum', forks_url: 'https://api.github.com/repos/danhellem/Lorem-ipsum/forks', diff --git a/src/github-pr.ts b/src/github-pr.ts index f0d8d86..a79302d 100644 --- a/src/github-pr.ts +++ b/src/github-pr.ts @@ -2,17 +2,8 @@ import {GitHub} from '@actions/github' import Payload from './viewmodels/payload' import {IResponse} from './interfaces/base-response' -export async function update( - payload: Payload, - token: string, - workItemId: number -): Promise { - const response: IResponse = { - code: 500, - message: 'failed', - success: false - } - +export async function update(payload: Payload, token: string, workItemId: number): Promise { + const response: IResponse = { code: 500, message: 'failed', success: false } const n = payload.body.includes(`AB#${workItemId}`) if (!n) { @@ -33,7 +24,7 @@ export async function update( response.success = true } } catch (error) { - response.message = error + response.message = JSON.stringify(error) } } else { response.code = 200 diff --git a/src/main.ts b/src/main.ts index f65017a..46588cb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,14 +11,14 @@ import {update as updatePr} from './github-pr' import {IResponse} from './interfaces/base-response' import * as patch from './patch-documents' -let debug = false -const local_debug = false -const ado_org = '' -const ado_project = '' -const ado_token = '' -const ado_wit = '' +let verbose_logging = false +const local_debug = false // set to true to fetch webhook json from local payload. For local debugging only!! +const ado_org = '{organization}' +const ado_project = '{project name}' +const ado_token = '{azure devops personal access token}' +const ado_wit = 'User Story' +const github_token = '{github token}' const ado_area_path = '' -const github_token = '' // prettier-ignore function getEnvInputs(): EnvInputs { @@ -32,7 +32,7 @@ function getEnvInputs(): EnvInputs { env.ado_active_state = process.env['ado_active_state'] !== undefined ? process.env['ado_active_state'] : 'Active' env.github_token = process.env['github_token'] !== undefined ? process.env['github_token'] : github_token env.ado_area_path = process.env['ado_area_path'] !== undefined ? process.env['ado_area_path'] : ado_area_path - if (! debug) debug = process.env['debug'] !== undefined ? true : false + if (! verbose_logging) verbose_logging = process.env['debug'] !== undefined ? true : false if (!env.ado_token) { console.log(' Missing ado_token value') } if (!env.ado_organization) { console.log(' Missing ado_organization value') } @@ -57,15 +57,15 @@ function getWebHookPayLoad(): Payload { payload.repo_url = body.repository?.html_url !== undefined ? body.repository.html_url : '' payload.repo_fullname = body.repository?.full_name !== undefined ? body.repository.full_name : '' payload.repo_owner = body.repository?.owner !== undefined ? body.repository.owner.login : '' - payload.sender_login = body.sender?.login !== undefined ? body.sender.login : '' - - let request_body: string = (body.pull_request?.body !== undefined || body.pull_request?.body !== null) ? body.pull_request?.body : '' - payload.body = (request_body !== null) ? request_body.replace(new RegExp('\\r?\\n','g'), '
') : '' + payload.sender_login = body.sender?.login !== undefined ? body.sender.login : '' + payload.body = (body.pull_request?.body !== undefined || body.pull_request?.body !== null) ? body.pull_request?.body : '' return payload } async function run(): Promise { + if (verbose_logging) console.log('WARNING! Verbose logging is turned on.'); + try { let workItem: WorkItem | null let workItemId: number @@ -74,13 +74,13 @@ async function run(): Promise { // set the env params const envInputs: EnvInputs = getEnvInputs() - if (debug) console.log(envInputs) + if (verbose_logging) console.log(envInputs) console.log('Getting payload values...') // get payload info const payload: Payload = getWebHookPayLoad() - if (debug) console.log(payload) + if (verbose_logging) console.log(payload) if (payload.sender_login === 'azure-boards[bot]') { console.log(`azure-boards[bot] sender, exiting action`) @@ -91,16 +91,14 @@ async function run(): Promise { // go and see if the ado work item already exists for this PR const fetchResult = await fetch(envInputs, payload) - if (debug) console.log(fetchResult) + if (verbose_logging) console.log(fetchResult) const response: IResponse = {code: 500, message: 'failed', success: false} // check to make sure your fetch was a success // success = return an work item or return a zero result if (!fetchResult.success) { - core.setFailed( - `Error fetching work item from Azure DevOps: ${fetchResult.message}` - ) + core.setFailed(`Error fetching work item from Azure DevOps: ${fetchResult.message}`) return } @@ -111,7 +109,7 @@ async function run(): Promise { // create work item const createResult = await create(envInputs, payload) - if (debug) console.log(createResult) + if (verbose_logging) console.log(createResult) // if we successfully created the work item, then go and // link the PR in GitHub to the PR work item in ADO @@ -121,22 +119,13 @@ async function run(): Promise { workItem = createResult.workItem workItemId = workItem?.id !== undefined ? workItem?.id : -1 - const pr: IResponse = - envInputs.github_token !== '' - ? await updatePr( - payload, - envInputs.github_token, - workItem?.id !== undefined ? workItem.id : -1 - ) - : response + const pr: IResponse = envInputs.github_token !== '' ? await updatePr(payload, envInputs.github_token, workItem?.id !== undefined ? workItem.id : -1) : response - if (debug) console.log(pr) + if (verbose_logging) console.log(pr) if (!pr.success) console.log(`Warning: ${pr.message}`) } else { - core.setFailed( - `Error creating work item in Azure DevOps: ${createResult.message}` - ) + core.setFailed(`Error creating work item in Azure DevOps: ${createResult.message}`) return } } else { @@ -156,70 +145,40 @@ async function run(): Promise { // check the action type and go do specific updates switch (payload.action) { case 'opened': { - const patchDocumentResponse: patch.IPatchDocumentResponse = patch.openedPatchDocument( - envInputs - ) + const patchDocumentResponse: patch.IPatchDocumentResponse = patch.openedPatchDocument(envInputs) // go update the work item to change the state // this gets the PR out of the new column and into something more actionable - if ( - patchDocumentResponse.success && - patchDocumentResponse !== undefined - ) { - const openedResult = await update( - envInputs, - workItemId, - patchDocumentResponse.patchDocument - ) - - if (debug) console.log(openedResult) + if (patchDocumentResponse.success && patchDocumentResponse !== undefined) { + const openedResult = await update(envInputs, workItemId, patchDocumentResponse.patchDocument) + + if (verbose_logging) console.log(openedResult) } break } case 'edited': { - const patchDocumentResponse: patch.IPatchDocumentResponse = patch.editedPatchDocument( - envInputs, - payload, - workItem - ) + const patchDocumentResponse: patch.IPatchDocumentResponse = patch.editedPatchDocument(envInputs, payload, workItem) // if success and patch document is not empty, then go update the work item - if ( - patchDocumentResponse.success && - patchDocumentResponse !== undefined - ) { - const updateResult = await update( - envInputs, - workItemId, - patchDocumentResponse.patchDocument - ) - - if (debug) console.log(updateResult) + if (patchDocumentResponse.success && patchDocumentResponse !== undefined) { + const updateResult = await update(envInputs, workItemId, patchDocumentResponse.patchDocument) + + if (verbose_logging) console.log(updateResult) } break } case 'closed': { - const patchDocumentResponse: patch.IPatchDocumentResponse = patch.closedPatchDocument( - envInputs, - payload - ) + const patchDocumentResponse: patch.IPatchDocumentResponse = patch.closedPatchDocument(envInputs, payload) // if success and patch document is not empty, then go update the work item - if ( - patchDocumentResponse.success && - patchDocumentResponse !== undefined - ) { - const closedResult = await update( - envInputs, - workItemId, - patchDocumentResponse.patchDocument - ) - - if (debug) console.log(closedResult) + if (patchDocumentResponse.success && patchDocumentResponse !== undefined) { + const closedResult = await update(envInputs, workItemId, patchDocumentResponse.patchDocument) + + if (verbose_logging) console.log(closedResult) } break diff --git a/src/patch-documents.ts b/src/patch-documents.ts index eae11b9..5956088 100644 --- a/src/patch-documents.ts +++ b/src/patch-documents.ts @@ -1,18 +1,13 @@ import {JsonPatchDocument} from 'azure-devops-node-api/interfaces/common/VSSInterfaces' import {WorkItem} from 'azure-devops-node-api/interfaces/WorkItemTrackingInterfaces' +import showdown from 'showdown' import EnvInputs from './viewmodels/env-inputs' import Payload from './viewmodels/payload' import {IResponse} from './interfaces/base-response' export function openedPatchDocument(env: EnvInputs): IPatchDocumentResponse { - const response: IPatchDocumentResponse = { - code: 200, - message: 'Success', - success: true, - patchDocument: undefined - } - + const response: IPatchDocumentResponse = { code: 200, message: 'Success', success: true, patchDocument: undefined } let patchDocument: JsonPatchDocument = [] patchDocument = [ @@ -28,57 +23,51 @@ export function openedPatchDocument(env: EnvInputs): IPatchDocumentResponse { return response } -export function editedPatchDocument( - env: EnvInputs, - payload: Payload, - workItem: WorkItem -): IPatchDocumentResponse { - const response: IPatchDocumentResponse = { - code: 500, - message: 'failed', - success: false, - patchDocument: undefined - } - - let patchDocument: JsonPatchDocument = [] - - const system_title: string = workItem.fields - ? workItem.fields['System.Title'] - : '' - const system_description: string = workItem.fields - ? workItem.fields['System.Description'] - : '' +export function editedPatchDocument(env: EnvInputs, payload: Payload, workItem: WorkItem): IPatchDocumentResponse { + const response: IPatchDocumentResponse = { code: 500, message: 'failed', success: false, patchDocument: undefined } + let patchDocument = [] - payload.body = payload.body.replace(`\r\nAB#${workItem.id}`, '') + const wi_title: string = workItem.fields ? workItem.fields['System.Title'] : '' + const wi_description: string = workItem.fields ? workItem.fields['System.Description']: '' + const wi_reprosteps: string = workItem.fields ? workItem.fields['Microsoft.VSTS.TCM.ReproSteps']: '' + + const body = payload.body.replace(`AB#${workItem.id}`, '').trim(); + const converter = new showdown.Converter(); + const html = converter.makeHtml(body); const pr_title = `${payload.title} (GitHub PR #${payload.number})` - const pr_desc = `${payload.body.trim()}

GitHub Pull Request #${payload.number} created in ${payload.repo_fullname}` + + if (wi_title != pr_title) { + patchDocument.push({ + op: "add", + path: "/fields/System.Title", + value: pr_title, + }); + } + + if (wi_description != html || wi_reprosteps != html ) { + patchDocument.push( + { + op: "add", + path: "/fields/System.Description", + value: html, + }, + { + op: "add", + path: "/fields/Microsoft.VSTS.TCM.ReproSteps", + value: html, + } + ); + } - if (system_title === pr_title && system_description === pr_desc) { + if (patchDocument.length <= 0) { response.code = 200 response.message = 'No updates made to work item title or description' response.success = true response.patchDocument = undefined return response - } - - patchDocument = [ - { - op: 'add', - path: '/fields/System.Title', - value: pr_title - }, - { - op: 'add', - path: '/fields/System.Description', - value: pr_desc - } - ] + } response.code = 200 response.message = 'Success' @@ -88,19 +77,9 @@ export function editedPatchDocument( return response } -export function closedPatchDocument( - env: EnvInputs, - payload: Payload -): IPatchDocumentResponse { - const response: IPatchDocumentResponse = { - code: 500, - message: 'failed', - success: false, - patchDocument: undefined - } - +export function closedPatchDocument(env: EnvInputs, payload: Payload): IPatchDocumentResponse { + const response: IPatchDocumentResponse = { code: 500, message: 'failed', success: false, patchDocument: undefined } const pr_comment = `GitHub Pull Request #${payload.number} was closed` - let patchDocument: JsonPatchDocument = [] patchDocument = [ diff --git a/src/workitems.ts b/src/workitems.ts index ab97ae1..cf62661 100644 --- a/src/workitems.ts +++ b/src/workitems.ts @@ -1,32 +1,18 @@ import * as azdev from 'azure-devops-node-api' -import {IWorkItemTrackingApi} from 'azure-devops-node-api/WorkItemTrackingApi' -import { - WorkItem, - WorkItemQueryResult, - WorkItemReference -} from 'azure-devops-node-api/interfaces/WorkItemTrackingInterfaces' - -import {IResponse} from './interfaces/base-response' +import { IWorkItemTrackingApi } from 'azure-devops-node-api/WorkItemTrackingApi' +import { WorkItem, WorkItemQueryResult, WorkItemReference } from 'azure-devops-node-api/interfaces/WorkItemTrackingInterfaces' +import { JsonPatchDocument } from 'azure-devops-node-api/interfaces/common/VSSInterfaces' +import showdown from 'showdown' + +import { IResponse } from './interfaces/base-response' import Payload from './viewmodels/payload' import EnvInputs from './viewmodels/env-inputs' -import {JsonPatchDocument} from 'azure-devops-node-api/interfaces/common/VSSInterfaces' - -export async function fetch( - env: EnvInputs, - payload: Payload -): Promise { - const response: IFetchResponse = { - code: 500, - message: 'failed', - success: false, - workItem: null - } + +export async function fetch(env: EnvInputs, payload: Payload): Promise { + const response: IFetchResponse = { code: 500, message: 'failed', success: false, workItem: null } const authHandler = azdev.getPersonalAccessTokenHandler(env.ado_token) - const connection = new azdev.WebApi( - `https://dev.azure.com/${env.ado_organization}`, - authHandler - ) + const connection = new azdev.WebApi(`https://dev.azure.com/${env.ado_organization}`, authHandler) try { response.code = 500 @@ -43,24 +29,17 @@ export async function fetch( response.code = 500 response.message = 'Error calling queryByWiql: ' - const queryResult: WorkItemQueryResult = await client.queryByWiql( - wiql, - teamContext - ) + const queryResult: WorkItemQueryResult = await client.queryByWiql(wiql, teamContext) // if query results = null then i think we have issue with the project name if (queryResult === null) { response.code = 500 - response.message = - 'Error empty queryResult: Project name appears to be invalid or query is not formed correctly' + response.message = 'Error empty queryResult: Project name appears to be invalid or query is not formed correctly' return response } // check to see if the query returned any results - const workItemReference: WorkItemReference | null = - queryResult.workItems !== null && queryResult.workItems !== undefined - ? queryResult.workItems[0] - : null + const workItemReference: WorkItemReference | null = queryResult.workItems !== null && queryResult.workItems !== undefined ? queryResult.workItems[0] : null // if we have results, fetch the work item if (workItemReference !== null && workItemReference !== undefined) { @@ -68,13 +47,7 @@ export async function fetch( response.message = 'Error calling getWorkItem: ' // fetch work item by id - const item: WorkItem = await client.getWorkItem( - workItemReference.id !== undefined ? workItemReference.id : 0, - undefined, - undefined, - undefined, - env.ado_project - ) + const item: WorkItem = await client.getWorkItem(workItemReference.id !== undefined ? workItemReference.id : 0, undefined, undefined, undefined, env.ado_project) response.code = 200 response.message = 'Success' @@ -99,16 +72,8 @@ export async function fetch( } } -export async function create( - env: EnvInputs, - payload: Payload -): Promise { - const response: IFetchResponse = { - code: 500, - message: 'failed', - success: false, - workItem: null - } +export async function create(env: EnvInputs, payload: Payload): Promise { + const response: IFetchResponse = { code: 500, message: 'failed', success: false, workItem: null } // prettier-ignore const patchDocument: JsonPatchDocument | any = [ @@ -127,11 +92,6 @@ export async function create( path: '/fields/System.History', value: `GitHub Pull Request #${payload.number} created in ${payload.repo_fullname}` }, - { - op: "add", - path: "/fields/System.Description", - value: payload.body.trim() - }, { op: 'add', path: '/relations/-', @@ -139,9 +99,36 @@ export async function create( rel: 'Hyperlink', url: payload.url } - } + }, + { + op: "add", + path: "/fields/Microsoft.VSTS.Common.StackRank", + value: 1 + }, + { + op: "add", + path: "/fields/Microsoft.VSTS.Common.BacklogPriority", + value: 1 + } ] + // if there is text, then save it to the description + if (payload.body.length > 0) { + const converter = new showdown.Converter(); + const html = converter.makeHtml(payload.body); + + patchDocument.push({ + op: "add", + path: "/fields/System.Description", + value: html + }, + { + op: "add", + path: "/fields/Microsoft.VSTS.TCM.ReproSteps", + value: html + }) + } + if (env.ado_area_path !== '') { patchDocument.push({ op: 'add', @@ -151,29 +138,18 @@ export async function create( } const authHandler = azdev.getPersonalAccessTokenHandler(env.ado_token) - const connection = new azdev.WebApi( - `https://dev.azure.com/${env.ado_organization}`, - authHandler - ) + const connection = new azdev.WebApi(`https://dev.azure.com/${env.ado_organization}`, authHandler) try { response.message = 'Error calling getWorkItemTrackingApi: ' const client: IWorkItemTrackingApi = await connection.getWorkItemTrackingApi() //create work item - const workItem: WorkItem = await client.createWorkItem( - [], - patchDocument, - env.ado_project, - env.ado_wit, - false, - false - ) + const workItem: WorkItem = await client.createWorkItem([], patchDocument, env.ado_project, env.ado_wit, false, false) // check to see if the work item is null or undefined if (workItem === null || workItem === undefined) { - response.message = - 'Error creating work item: Work item is null or undefined' + response.message = 'Error creating work item: Work item is null or undefined' } else { response.code = 200 response.message = 'Success' @@ -191,46 +167,26 @@ export async function create( } } -export async function update( - env: EnvInputs, - workItemId: number, - patchDocument: JsonPatchDocument | undefined -): Promise { - const response: IFetchResponse = { - code: 500, - message: 'failed', - success: false, - workItem: null - } +export async function update(env: EnvInputs, workItemId: number, patchDocument: JsonPatchDocument | undefined): Promise { + const response: IFetchResponse = { code: 500, message: 'failed', success: false, workItem: null } if (patchDocument === undefined) { return response } const authHandler = azdev.getPersonalAccessTokenHandler(env.ado_token) - const connection = new azdev.WebApi( - `https://dev.azure.com/${env.ado_organization}`, - authHandler - ) + const connection = new azdev.WebApi(`https://dev.azure.com/${env.ado_organization}`, authHandler) try { response.message = 'Error calling getWorkItemTrackingApi: ' const client: IWorkItemTrackingApi = await connection.getWorkItemTrackingApi() //create work item - const workItemResult: WorkItem = await client.updateWorkItem( - [], - patchDocument, - workItemId, - env.ado_project, - false, - false - ) + const workItemResult: WorkItem = await client.updateWorkItem([], patchDocument, workItemId, env.ado_project, false, false) // check to see if the work item is null or undefined if (workItemResult === null || workItemResult === undefined) { - response.message = - 'Error updating work item: Work item result is null or undefined' + response.message = 'Error updating work item: Work item result is null or undefined' } else { response.code = 200 response.message = 'Success'