From d86fda9648674121106d6d93cd8b6ce4dc848541 Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Sun, 24 Oct 2021 19:29:03 +0200 Subject: [PATCH 01/10] Github [skip ci] --- bin/cml/send-comment.js | 5 ++ src/cml.js | 58 +++++++++++-- src/drivers/bitbucket_cloud.js | 144 ++++++++++++++++----------------- src/drivers/github.js | 109 ++++++++++++++++++------- src/drivers/gitlab.js | 38 ++++++++- 5 files changed, 243 insertions(+), 111 deletions(-) diff --git a/bin/cml/send-comment.js b/bin/cml/send-comment.js index 08b13f0b2..a2de7bcb1 100644 --- a/bin/cml/send-comment.js +++ b/bin/cml/send-comment.js @@ -16,6 +16,11 @@ exports.handler = async (opts) => { exports.builder = (yargs) => yargs.env('CML_SEND_COMMENT').options( kebabcaseKeys({ + pr: { + type: 'boolean', + description: + 'Creates the comment as a PR/MR comment given by the commit-sha' + }, commitSha: { type: 'string', alias: 'head-sha', diff --git a/src/cml.js b/src/cml.js index d330f89c4..29d74c787 100755 --- a/src/cml.js +++ b/src/cml.js @@ -95,20 +95,64 @@ class CML { report: userReport, commitSha = await this.headSha(), rmWatermark, - update + update, + pr } = opts; + if (rmWatermark && update) throw new Error('watermarks are mandatory for updateable comments'); + const watermark = rmWatermark ? '' - : ' \n\n ![CML watermark](https://raw.githubusercontent.com/iterative/cml/master/assets/watermark.svg)'; - const report = `${userReport}${watermark}`; + : `![CML watermark](https://raw.githubusercontent.com/iterative/cml/master/assets/watermark.svg)`; + + const report = `${userReport}\n\n${watermark}`; + const drv = getDriver(this); + + let comment; + const updatableComment = (comments) => + comments.reverse().find(({ body }) => { + return body.includes(watermark); + }); + + if (pr || this.driver === 'bitbucket') { + const branch = (await exec(`git branch --contains ${commitSha}`)).replace( + '* ', + '' + ); + const prs = await drv.prs(); + const { url } = prs.find((pr) => pr.source === branch) || {}; + + if (!url) throw new Error(`PR for branch "${branch}" not found`); + + const [prNumber] = url.split('/').slice(-1); + + if (update) + comment = updatableComment(await drv.prComments({ prNumber })); + + if (update && comment) { + return await drv.prCommentUpdate({ + report, + id: comment.id + }); + } + + return await drv.prCommentCreate({ report, prNumber }); + } + + if (update) + comment = updatableComment(await drv.commitComments({ commitSha })); + + if (update && comment) { + return await drv.commentUpdate({ + report, + id: comment.id + }); + } - return await getDriver(this).commentCreate({ - ...opts, + return await drv.commentCreate({ report, - commitSha, - watermark + commitSha }); } diff --git a/src/drivers/bitbucket_cloud.js b/src/drivers/bitbucket_cloud.js index 8f3489c65..bc6350a65 100644 --- a/src/drivers/bitbucket_cloud.js +++ b/src/drivers/bitbucket_cloud.js @@ -23,69 +23,46 @@ class BitbucketCloud { async commentCreate(opts = {}) { const { projectPath } = this; - const { commitSha, report, update, watermark } = opts; + const { commitSha, report } = opts; - // Check for a corresponding PR. If it exists, also put the comment there. - let prs; - try { - const getPrEndpoint = `/repositories/${projectPath}/commit/${commitSha}/pullrequests`; - prs = await this.paginatedRequest({ endpoint: getPrEndpoint }); - } catch (err) { - if (err.message === 'Not Found Resource not found') - err.message = - "Click 'Go to pull request' on any commit details page to enable this API"; - throw err; - } - - if (prs && prs.length) { - for (const pr of prs) { - // Append a watermark to the report with a link to the commit - const commitLink = commitSha.substr(0, 7); - const longReport = `${commitLink}\n\n${report}`; - const prBody = JSON.stringify({ content: { raw: longReport } }); - - // Write a comment on the PR - const prEndpoint = `/repositories/${projectPath}/pullrequests/${pr.id}/comments/`; - const existingPr = ( - await this.paginatedRequest({ endpoint: prEndpoint, method: 'GET' }) - ) - .filter((comment) => { - const { content: { raw = '' } = {} } = comment; - return raw.endsWith(watermark); - }) - .sort((first, second) => first.id < second.id) - .pop(); - await this.request({ - endpoint: prEndpoint + (update && existingPr ? existingPr.id : ''), - method: update && existingPr ? 'PUT' : 'POST', - body: prBody - }); - } - } + const endpoint = `/repositories/${projectPath}/commit/${commitSha}/comments/`; + return ( + await this.request({ + endpoint, + method: 'PUT', + body: JSON.stringify({ content: { raw: report } }) + }) + ).links.html.href; + } - const commitEndpoint = `/repositories/${projectPath}/commit/${commitSha}/comments/`; + async commentUpdate(opts = {}) { + const { projectPath } = this; + const { commitSha, report, id } = opts; - const existingCommmit = ( - await this.paginatedRequest({ endpoint: commitEndpoint, method: 'GET' }) - ) - .filter((comment) => { - const { content: { raw = '' } = {} } = comment; - return raw.endsWith(watermark); - }) - .sort((first, second) => first.id < second.id) - .pop(); + const endpoint = `/repositories/${projectPath}/commit/${commitSha}/comments/${id}`; return ( await this.request({ - endpoint: - commitEndpoint + - (update && existingCommmit ? existingCommmit.id : ''), - method: update && existingCommmit ? 'PUT' : 'POST', + endpoint, + method: 'POST', body: JSON.stringify({ content: { raw: report } }) }) ).links.html.href; } + async commitComments(opts = {}) { + const { projectPath } = this; + const { commitSha } = opts; + + const endpoint = `/repositories/${projectPath}/commit/${commitSha}/comments/`; + + return await this.paginatedRequest({ endpoint, method: 'GET' }).map( + ({ id, content: { raw: body = '' } = {} }) => { + return { id, body }; + } + ); + } + async checkCreate() { throw new Error('Bitbucket Cloud does not support check!'); } @@ -142,31 +119,52 @@ class BitbucketCloud { return href; } + async prCommentCreate(opts = {}) { + const { projectPath } = this; + const { description, prNumber, prId } = opts; + + const endpoint = `/repositories/${projectPath}/pullrequests/${ + prId || prNumber + }/comments/`; + await this.request({ + endpoint, + method: 'PUT', + body: JSON.stringify({ content: { raw: description } }) + }); + } + async prs(opts = {}) { const { projectPath } = this; const { state = 'OPEN' } = opts; - const endpoint = `/repositories/${projectPath}/pullrequests?state=${state}`; - const { values: prs } = await this.request({ endpoint }); - - return prs.map((pr) => { - const { - links: { - html: { href: url } - }, - source: { - branch: { name: source } - }, - destination: { - branch: { name: target } - } - } = pr; - return { - url, - source, - target - }; - }); + try { + const endpoint = `/repositories/${projectPath}/pullrequests?state=${state}`; + const { values: prs } = await this.paginatedRequest({ endpoint }); + + return prs.map((pr) => { + const { + links: { + html: { href: url } + }, + source: { + branch: { name: source } + }, + destination: { + branch: { name: target } + } + } = pr; + return { + url, + source, + target + }; + }); + } catch (err) { + if (err.message === 'Not Found Resource not found') + err.message = + "Click 'Go to pull request' on any commit details page to enable this API"; + throw err; + } } async pipelineRestart(opts = {}) { diff --git a/src/drivers/github.js b/src/drivers/github.js index 6ca5ab0e3..554434be9 100644 --- a/src/drivers/github.js +++ b/src/drivers/github.js @@ -90,40 +90,43 @@ class Github { } async commentCreate(opts = {}) { - const { report: body, commitSha, update, watermark } = opts; + const { report: body, commitSha } = opts; + const { repos } = octokit(this.token, this.repo); - const { paginate, repos } = octokit(this.token, this.repo); + return ( + await repos.createCommitComment({ + ...ownerRepo({ uri: this.repo }), + commit_sha: commitSha, + body + }) + ).data.html_url; + } - const existing = Object.values( + async commentUpdate(opts = {}) { + const { report: body, id } = opts; + const { repos } = octokit(this.token, this.repo); + + return ( + await repos.updateCommitComment({ + ...ownerRepo({ uri: this.repo }), + comment_id: id, + body + }) + ).data.html_url; + } + + async commitComments(opts = {}) { + const { commitSha } = opts; + const { repos, paginate } = octokit(this.token, this.repo); + + return ( await paginate(repos.listCommentsForCommit, { ...ownerRepo({ uri: this.repo }), commit_sha: commitSha }) - ) - .filter((comment) => { - const { body = '' } = comment; - return body.endsWith(watermark); - }) - .sort((first, second) => first.id < second.id) - .pop(); - - if (update && existing) { - return ( - await repos.updateCommitComment({ - ...ownerRepo({ uri: this.repo }), - comment_id: existing.id, - body - }) - ).data.html_url; - } else { - return ( - await repos.createCommitComment({ - ...ownerRepo({ uri: this.repo }), - commit_sha: commitSha, - body - }) - ).data.html_url; - } + ).map(({ id, body }) => { + return { id, body }; + }); } async checkCreate(opts = {}) { @@ -291,6 +294,56 @@ class Github { return htmlUrl; } + async prCommentCreate(opts = {}) { + const { report: body, prNumber } = opts; + const { owner, repo } = ownerRepo({ uri: this.repo }); + const { issues } = octokit(this.token, this.repo); + + const { + data: { html_url: htmlUrl } + } = await issues.createComment({ + owner, + repo, + body, + issue_number: prNumber + }); + + return htmlUrl; + } + + async prCommentUpdate(opts = {}) { + const { report: body, id } = opts; + const { owner, repo } = ownerRepo({ uri: this.repo }); + const { issues } = octokit(this.token, this.repo); + + const { + data: { html_url: htmlUrl } + } = await issues.updateComment({ + owner, + repo, + body, + comment_id: id + }); + + return htmlUrl; + } + + async prComments(opts = {}) { + const { prNumber } = opts; + const { owner, repo } = ownerRepo({ uri: this.repo }); + const { issues } = octokit(this.token, this.repo); + + const { data: comments } = await issues.listComments({ + owner, + repo, + issue_number: prNumber + }); + + return comments.map(({ id, body }) => { + return { id, body }; + }); + } + async prs(opts = {}) { const { state = 'open' } = opts; const { owner, repo } = ownerRepo({ uri: this.repo }); diff --git a/src/drivers/gitlab.js b/src/drivers/gitlab.js index c37e488d9..198b0b436 100644 --- a/src/drivers/gitlab.js +++ b/src/drivers/gitlab.js @@ -70,9 +70,7 @@ class Gitlab { } async commentCreate(opts = {}) { - const { commitSha, report, update } = opts; - - if (update) throw new Error('GitLab does not support comment updates!'); + const { commitSha, report } = opts; const projectPath = await this.projectPath(); const endpoint = `/projects/${projectPath}/repository/commits/${commitSha}/comments`; @@ -84,6 +82,23 @@ class Gitlab { return output; } + async commentUpdate(opts = {}) { + throw new Error('GitLab does not support comment updates!'); + } + + async commitComments(opts = {}) { + const { commitSha } = opts; + + const projectPath = await this.projectPath(); + const endpoint = `/projects/${projectPath}/repository/commits/${commitSha}/comments`; + + return (await this.request({ endpoint, method: 'GET' })).map( + ({ id, note: body }) => { + return { id, body }; + } + ); + } + async checkCreate() { throw new Error('Gitlab does not support check!'); } @@ -213,6 +228,23 @@ class Gitlab { return url; } + async prCommentCreate(opts = {}) { + const projectPath = await this.projectPath(); + const { description, prNumber } = opts; + + const endpoint = `/projects/${projectPath}/merge_requests/${prNumber}/notes`; + const body = new URLSearchParams(); + body.append('body', description); + + const { id } = await this.request({ + endpoint, + method: 'POST', + body + }); + + return `${this.repo}/-/merge_requests/${prNumber}#note_${id}`; + } + async prs(opts = {}) { const projectPath = await this.projectPath(); const { state = 'opened' } = opts; From 5ec7cbd6073efd2b58b99f2474a38be1fcfdf0e2 Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Sun, 24 Oct 2021 21:53:18 +0200 Subject: [PATCH 02/10] gitlab --- src/cml.js | 25 ++++++++++++--------- src/drivers/gitlab.js | 51 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/cml.js b/src/cml.js index 29d74c787..2cd943515 100755 --- a/src/cml.js +++ b/src/cml.js @@ -110,16 +110,16 @@ class CML { const drv = getDriver(this); let comment; - const updatableComment = (comments) => - comments.reverse().find(({ body }) => { - return body.includes(watermark); + const updatableComment = (comments) => { + return comments.reverse().find(({ body }) => { + return body.includes('watermark.svg'); }); + }; if (pr || this.driver === 'bitbucket') { - const branch = (await exec(`git branch --contains ${commitSha}`)).replace( - '* ', - '' - ); + const branch = (await exec(`git branch --contains ${commitSha}`)) + .replace('*', '') + .trim(); const prs = await drv.prs(); const { url } = prs.find((pr) => pr.source === branch) || {}; @@ -131,13 +131,18 @@ class CML { comment = updatableComment(await drv.prComments({ prNumber })); if (update && comment) { - return await drv.prCommentUpdate({ + const commentUrl = await drv.prCommentUpdate({ report, - id: comment.id + id: comment.id, + prNumber }); + + if (this.driver !== 'bitbucket') return commentUrl; } - return await drv.prCommentCreate({ report, prNumber }); + const commentUrl = await drv.prCommentCreate({ report, prNumber }); + + if (this.driver !== 'bitbucket') return commentUrl; } if (update) diff --git a/src/drivers/gitlab.js b/src/drivers/gitlab.js index 198b0b436..353430abd 100644 --- a/src/drivers/gitlab.js +++ b/src/drivers/gitlab.js @@ -77,9 +77,9 @@ class Gitlab { const body = new URLSearchParams(); body.append('note', report); - const output = await this.request({ endpoint, method: 'POST', body }); + await this.request({ endpoint, method: 'POST', body }); - return output; + return `${this.repo}/-/commit/${commitSha}`; } async commentUpdate(opts = {}) { @@ -92,11 +92,11 @@ class Gitlab { const projectPath = await this.projectPath(); const endpoint = `/projects/${projectPath}/repository/commits/${commitSha}/comments`; - return (await this.request({ endpoint, method: 'GET' })).map( - ({ id, note: body }) => { - return { id, body }; - } - ); + const comments = await this.request({ endpoint, method: 'GET' }); + + return comments.map(({ id, note: body }) => { + return { id, body }; + }); } async checkCreate() { @@ -230,11 +230,11 @@ class Gitlab { async prCommentCreate(opts = {}) { const projectPath = await this.projectPath(); - const { description, prNumber } = opts; + const { report, prNumber } = opts; const endpoint = `/projects/${projectPath}/merge_requests/${prNumber}/notes`; const body = new URLSearchParams(); - body.append('body', description); + body.append('body', report); const { id } = await this.request({ endpoint, @@ -245,6 +245,39 @@ class Gitlab { return `${this.repo}/-/merge_requests/${prNumber}#note_${id}`; } + async prCommentUpdate(opts = {}) { + const projectPath = await this.projectPath(); + const { report, prNumber, id: commentId } = opts; + + const endpoint = `/projects/${projectPath}/merge_requests/${prNumber}/notes/${commentId}`; + const body = new URLSearchParams(); + body.append('body', report); + + const { id } = await this.request({ + endpoint, + method: 'PUT', + body + }); + + return `${this.repo}/-/merge_requests/${prNumber}#note_${id}`; + } + + async prComments(opts = {}) { + const projectPath = await this.projectPath(); + const { prNumber } = opts; + + const endpoint = `/projects/${projectPath}/merge_requests/${prNumber}/notes`; + + const comments = await this.request({ + endpoint, + method: 'GET' + }); + + return comments.map(({ id, body }) => { + return { id, body }; + }); + } + async prs(opts = {}) { const projectPath = await this.projectPath(); const { state = 'opened' } = opts; From 4e1a4d20f4c4dfe7ee9feddcbd93fff45ff9081b Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Mon, 25 Oct 2021 01:14:48 +0200 Subject: [PATCH 03/10] bitbucket --- src/cml.js | 18 +++++++------ src/drivers/bitbucket_cloud.js | 47 ++++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/cml.js b/src/cml.js index 2cd943515..a98cfa314 100755 --- a/src/cml.js +++ b/src/cml.js @@ -51,6 +51,7 @@ const inferDriver = (opts = {}) => { if (repo && repo.includes('github.com')) return GITHUB; if (repo && repo.includes('gitlab.com')) return GITLAB; if (repo && repo.includes('bitbucket.com')) return BB; + if (repo && repo.includes('bitbucket.org')) return BB; if (GITHUB_REPOSITORY) return GITHUB; if (CI_PROJECT_URL) return GITLAB; @@ -117,9 +118,10 @@ class CML { }; if (pr || this.driver === 'bitbucket') { - const branch = (await exec(`git branch --contains ${commitSha}`)) - .replace('*', '') - .trim(); + const branch = + (await exec(`git branch --contains ${commitSha}`)) + .replace('*', '') + .trim() || (await this.branch()); const prs = await drv.prs(); const { url } = prs.find((pr) => pr.source === branch) || {}; @@ -137,12 +139,11 @@ class CML { prNumber }); + if (this.driver !== 'bitbucket') return commentUrl; + } else { + const commentUrl = await drv.prCommentCreate({ report, prNumber }); if (this.driver !== 'bitbucket') return commentUrl; } - - const commentUrl = await drv.prCommentCreate({ report, prNumber }); - - if (this.driver !== 'bitbucket') return commentUrl; } if (update) @@ -151,7 +152,8 @@ class CML { if (update && comment) { return await drv.commentUpdate({ report, - id: comment.id + id: comment.id, + commitSha }); } diff --git a/src/drivers/bitbucket_cloud.js b/src/drivers/bitbucket_cloud.js index bc6350a65..b26adec57 100644 --- a/src/drivers/bitbucket_cloud.js +++ b/src/drivers/bitbucket_cloud.js @@ -29,7 +29,7 @@ class BitbucketCloud { return ( await this.request({ endpoint, - method: 'PUT', + method: 'POST', body: JSON.stringify({ content: { raw: report } }) }) ).links.html.href; @@ -40,11 +40,10 @@ class BitbucketCloud { const { commitSha, report, id } = opts; const endpoint = `/repositories/${projectPath}/commit/${commitSha}/comments/${id}`; - return ( await this.request({ endpoint, - method: 'POST', + method: 'PUT', body: JSON.stringify({ content: { raw: report } }) }) ).links.html.href; @@ -56,7 +55,7 @@ class BitbucketCloud { const endpoint = `/repositories/${projectPath}/commit/${commitSha}/comments/`; - return await this.paginatedRequest({ endpoint, method: 'GET' }).map( + return (await this.paginatedRequest({ endpoint, method: 'GET' })).map( ({ id, content: { raw: body = '' } = {} }) => { return { id, body }; } @@ -121,16 +120,42 @@ class BitbucketCloud { async prCommentCreate(opts = {}) { const { projectPath } = this; - const { description, prNumber, prId } = opts; + const { report, prNumber } = opts; + + const endpoint = `/repositories/${projectPath}/pullrequests/${prNumber}/comments/`; + const output = await this.request({ + endpoint, + method: 'POST', + body: JSON.stringify({ content: { raw: report } }) + }); + + return output.links.self.href; + } + + async prCommentUpdate(opts = {}) { + const { projectPath } = this; + const { report, prNumber, id } = opts; - const endpoint = `/repositories/${projectPath}/pullrequests/${ - prId || prNumber - }/comments/`; - await this.request({ + const endpoint = `/repositories/${projectPath}/pullrequests/${prNumber}/comments/${id}`; + const output = await this.request({ endpoint, method: 'PUT', - body: JSON.stringify({ content: { raw: description } }) + body: JSON.stringify({ content: { raw: report } }) }); + + return output.links.self.href; + } + + async prComments(opts = {}) { + const { projectPath } = this; + const { prNumber } = opts; + + const endpoint = `/repositories/${projectPath}/pullrequests/${prNumber}/comments/`; + return (await this.paginatedRequest({ endpoint, method: 'GET' })).map( + ({ id, content: { raw: body = '' } = {} }) => { + return { id, body }; + } + ); } async prs(opts = {}) { @@ -139,7 +164,7 @@ class BitbucketCloud { try { const endpoint = `/repositories/${projectPath}/pullrequests?state=${state}`; - const { values: prs } = await this.paginatedRequest({ endpoint }); + const prs = await this.paginatedRequest({ endpoint }); return prs.map((pr) => { const { From 49fc0caae1ead8eb6abb2661a7c3d72eaf273928 Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Mon, 25 Oct 2021 01:50:05 +0200 Subject: [PATCH 04/10] commitPrs --- src/cml.js | 12 ++++-------- src/drivers/bitbucket_cloud.js | 26 ++++++++++++++++++++++++++ src/drivers/github.js | 24 ++++++++++++++++++++++++ src/drivers/gitlab.js | 18 ++++++++++++++++++ 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/cml.js b/src/cml.js index a98cfa314..f9abb86f5 100755 --- a/src/cml.js +++ b/src/cml.js @@ -118,14 +118,10 @@ class CML { }; if (pr || this.driver === 'bitbucket') { - const branch = - (await exec(`git branch --contains ${commitSha}`)) - .replace('*', '') - .trim() || (await this.branch()); - const prs = await drv.prs(); - const { url } = prs.find((pr) => pr.source === branch) || {}; - - if (!url) throw new Error(`PR for branch "${branch}" not found`); + const [commitPr] = await drv.commitPrs({ commitSha }); + const { url } = commitPr; + + if (!url) throw new Error(`PR for commit sha "${commitSha}" not found`); const [prNumber] = url.split('/').slice(-1); diff --git a/src/drivers/bitbucket_cloud.js b/src/drivers/bitbucket_cloud.js index b26adec57..c89998f55 100644 --- a/src/drivers/bitbucket_cloud.js +++ b/src/drivers/bitbucket_cloud.js @@ -62,6 +62,32 @@ class BitbucketCloud { ); } + async commitPrs(opts = {}) { + const { projectPath } = this; + const { commitSha, state = 'OPEN' } = opts; + + try { + const endpoint = `/repositories/${projectPath}/commit/${commitSha}/pullrequests?state=${state}`; + const prs = await this.paginatedRequest({ endpoint }); + + return prs.map((pr) => { + const { + links: { + html: { href: url } + } + } = pr; + return { + url + }; + }); + } catch (err) { + if (err.message === 'Not Found Resource not found') + err.message = + "Click 'Go to pull request' on any commit details page to enable this API"; + throw err; + } + } + async checkCreate() { throw new Error('Bitbucket Cloud does not support check!'); } diff --git a/src/drivers/github.js b/src/drivers/github.js index 554434be9..4d7283800 100644 --- a/src/drivers/github.js +++ b/src/drivers/github.js @@ -129,6 +129,30 @@ class Github { }); } + async commitPrs(opts = {}) { + const { commitSha, state = 'open' } = opts; + const { repos } = octokit(this.token, this.repo); + + return ( + await repos.listPullRequestsAssociatedWithCommit({ + ...ownerRepo({ uri: this.repo }), + commit_sha: commitSha, + state + }) + ).data.map((pr) => { + const { + html_url: url, + head: { ref: source }, + base: { ref: target } + } = pr; + return { + url, + source: branchName(source), + target: branchName(target) + }; + }); + } + async checkCreate(opts = {}) { const { report, diff --git a/src/drivers/gitlab.js b/src/drivers/gitlab.js index 353430abd..af8fe5741 100644 --- a/src/drivers/gitlab.js +++ b/src/drivers/gitlab.js @@ -99,6 +99,24 @@ class Gitlab { }); } + async commitPrs(opts = {}) { + const { commitSha } = opts; + + const projectPath = await this.projectPath(); + + const endpoint = `/projects/${projectPath}/repository/commits/${commitSha}/merge_requests`; + const prs = await this.request({ endpoint, method: 'GET' }); + + return prs.map((pr) => { + const { web_url: url, source_branch: source, target_branch: target } = pr; + return { + url, + source, + target + }; + }); + } + async checkCreate() { throw new Error('Gitlab does not support check!'); } From 92f713449c23ecde48ebd7b6d733bdee1dc7e375 Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Sun, 24 Oct 2021 23:58:35 +0000 Subject: [PATCH 05/10] snapshots --- bin/cml/send-comment.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/cml/send-comment.test.js b/bin/cml/send-comment.test.js index a4f5a3584..288764858 100644 --- a/bin/cml/send-comment.test.js +++ b/bin/cml/send-comment.test.js @@ -23,6 +23,8 @@ Options: --version Show version number [boolean] --log Maximum log level [string] [choices: \\"error\\", \\"warn\\", \\"info\\", \\"debug\\"] [default: \\"info\\"] + --pr Creates the comment as a PR/MR comment given by the + commit-sha [boolean] --commit-sha, --head-sha Commit SHA linked to this comment. Defaults to HEAD. [string] --update Update the last CML comment (if any) instead of From 5650d5d877153b6a55346a32ab8954328b7eddd5 Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Mon, 25 Oct 2021 00:13:05 +0000 Subject: [PATCH 06/10] fix tests --- src/drivers/bitbucket_cloud.test.js | 3 ++- src/drivers/github.test.js | 3 ++- src/drivers/gitlab.test.js | 5 ++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/drivers/bitbucket_cloud.test.js b/src/drivers/bitbucket_cloud.test.js index d49f69c73..64853ad0e 100644 --- a/src/drivers/bitbucket_cloud.test.js +++ b/src/drivers/bitbucket_cloud.test.js @@ -16,8 +16,9 @@ describe('Non Enviromental tests', () => { test('Comment', async () => { const report = '## Test comment'; const commitSha = SHA; + const url = await client.commentCreate({ report, commitSha }); - await client.commentCreate({ report, commitSha }); + expect(url.startsWith('https://')).toBe(true); }); test('Check', async () => { diff --git a/src/drivers/github.test.js b/src/drivers/github.test.js index 70c4e8ce5..6aa4bc2a2 100644 --- a/src/drivers/github.test.js +++ b/src/drivers/github.test.js @@ -22,8 +22,9 @@ describe('Non Enviromental tests', () => { test('Comment', async () => { const report = '## Test comment'; const commitSha = SHA; + const url = await client.commentCreate({ report, commitSha }); - await client.commentCreate({ report, commitSha }); + expect(url.startsWith('https://')).toBe(true); }); test('Publish', async () => { diff --git a/src/drivers/gitlab.test.js b/src/drivers/gitlab.test.js index c1cf136ce..4dfc5fc73 100644 --- a/src/drivers/gitlab.test.js +++ b/src/drivers/gitlab.test.js @@ -17,13 +17,12 @@ describe('Non Enviromental tests', () => { test('Comment', async () => { const report = '## Test comment'; const commitSha = SHA; - - const { created_at: createdAt } = await client.commentCreate({ + const url = await client.commentCreate({ report, commitSha }); - expect(createdAt).not.toBeUndefined(); + expect(url.startsWith('https://')).toBe(true); }); test('Check', async () => { From 9b36362d8008cad81ada6f1e4e8028b302d0ef5a Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Wed, 20 Oct 2021 18:01:00 +0200 Subject: [PATCH 07/10] [Snyk] Upgrade pseudoexec from 0.1.4 to 0.2.0 (#755) * fix: upgrade pseudoexec from 0.1.4 to 0.2.0 Snyk has created this PR to upgrade pseudoexec from 0.1.4 to 0.2.0. See this package in npm: https://www.npmjs.com/package/pseudoexec See this project in Snyk: https://app.snyk.io/org/casperdcl/project/58d0fcac-fe96-4a3b-b587-5fa62067cfa1 Note from @0x2b3bfa0: incidentally, this package is my brainchild and the only major difference between earlier versions and 0.2 is tests. https://github.com/0x2b3bfa0/node-pseudoexec Co-authored-by: Helio Machado <0x2b3bfa0+git@googlemail.com> --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d4fb0dc6..41f0cef5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7071,9 +7071,9 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "pseudoexec": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/pseudoexec/-/pseudoexec-0.1.4.tgz", - "integrity": "sha512-4FRBAbe36HjS4eocRH9yALjHwsiXHOt+OPl42Ss5UcaK82jvUZCWdkv5jBhhCXSBQL/aDx9qapX0taEHP5dZpg==" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/pseudoexec/-/pseudoexec-0.2.0.tgz", + "integrity": "sha512-sqVfdNs4INdT+O22R6qVFIWNwNjHguG7wSrSaF3efxIrw8ipgUhDVrGw+QysEV9OdsQYz4cqhqneLEf8Cfh2wA==" }, "psl": { "version": "1.8.0", diff --git a/package.json b/package.json index aceb1912c..b4d8872f0 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "node-forge": "^0.10.0", "node-ssh": "^12.0.0", "proxy-agent": "^5.0.0", - "pseudoexec": "^0.1.4", + "pseudoexec": "^0.2.0", "semver": "^7.3.5", "simple-git": "^2.45.1", "strip-url-auth": "^1.0.1", From ca6c60ffc2df7b10cce16a45040954bfbe25a4e9 Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Mon, 25 Oct 2021 15:57:25 +0200 Subject: [PATCH 08/10] long report in prs --- src/cml.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/cml.js b/src/cml.js index f9abb86f5..202fac09b 100755 --- a/src/cml.js +++ b/src/cml.js @@ -118,6 +118,9 @@ class CML { }; if (pr || this.driver === 'bitbucket') { + let commentUrl; + + const longReport = `${commitSha.substr(0, 7)}\n\n${report}`; const [commitPr] = await drv.commitPrs({ commitSha }); const { url } = commitPr; @@ -129,17 +132,18 @@ class CML { comment = updatableComment(await drv.prComments({ prNumber })); if (update && comment) { - const commentUrl = await drv.prCommentUpdate({ - report, + commentUrl = await drv.prCommentUpdate({ + report: longReport, id: comment.id, prNumber }); + } else + commentUrl = await drv.prCommentCreate({ + report: longReport, + prNumber + }); - if (this.driver !== 'bitbucket') return commentUrl; - } else { - const commentUrl = await drv.prCommentCreate({ report, prNumber }); - if (this.driver !== 'bitbucket') return commentUrl; - } + if (this.driver !== 'bitbucket') return commentUrl; } if (update) From 97a1442eff14965bf7defc174530a5ccf7d2a8ae Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Mon, 25 Oct 2021 18:16:54 +0200 Subject: [PATCH 09/10] fix tensorboard py3.10 (#777) * Fixes TB python 3.10 failure * py3.9 Co-authored-by: Olivaw[bot] Co-authored-by: Casper da Costa-Luis --- .github/workflows/test-deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml index 740f5ccee..40d3f0ceb 100644 --- a/.github/workflows/test-deploy.yml +++ b/.github/workflows/test-deploy.yml @@ -34,6 +34,8 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.ref }} - uses: actions/setup-node@v2 - uses: actions/setup-python@v2 + with: + python-version: 3.9 - run: pip install tensorboard - run: npm ci - run: npm run test From 03bba2b11f7bc152d40e5e0b0a7d6ca9630fc4f1 Mon Sep 17 00:00:00 2001 From: DavidGOrtega Date: Wed, 27 Oct 2021 08:39:27 +0200 Subject: [PATCH 10/10] Cli docs and minors Co-authored-by: Casper da Costa-Luis --- bin/cml/send-comment.js | 2 +- bin/cml/send-comment.test.js | 4 ++-- src/cml.js | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/bin/cml/send-comment.js b/bin/cml/send-comment.js index a2de7bcb1..621ab9368 100644 --- a/bin/cml/send-comment.js +++ b/bin/cml/send-comment.js @@ -19,7 +19,7 @@ exports.builder = (yargs) => pr: { type: 'boolean', description: - 'Creates the comment as a PR/MR comment given by the commit-sha' + 'Post to an existing PR/MR associated with the specified commit' }, commitSha: { type: 'string', diff --git a/bin/cml/send-comment.test.js b/bin/cml/send-comment.test.js index 288764858..7cf1aa7a5 100644 --- a/bin/cml/send-comment.test.js +++ b/bin/cml/send-comment.test.js @@ -23,8 +23,8 @@ Options: --version Show version number [boolean] --log Maximum log level [string] [choices: \\"error\\", \\"warn\\", \\"info\\", \\"debug\\"] [default: \\"info\\"] - --pr Creates the comment as a PR/MR comment given by the - commit-sha [boolean] + --pr Post to an existing PR/MR associated with the + specified commit [boolean] --commit-sha, --head-sha Commit SHA linked to this comment. Defaults to HEAD. [string] --update Update the last CML comment (if any) instead of diff --git a/src/cml.js b/src/cml.js index 202fac09b..90f6b9037 100755 --- a/src/cml.js +++ b/src/cml.js @@ -48,10 +48,11 @@ const inferToken = () => { const inferDriver = (opts = {}) => { const { repo } = opts; - if (repo && repo.includes('github.com')) return GITHUB; - if (repo && repo.includes('gitlab.com')) return GITLAB; - if (repo && repo.includes('bitbucket.com')) return BB; - if (repo && repo.includes('bitbucket.org')) return BB; + if (repo) { + if (repo.includes('github.com')) return GITHUB; + if (repo.includes('gitlab.com')) return GITLAB; + if (/bitbucket\.(com|org)/.test(repo)) return BB; + } if (GITHUB_REPOSITORY) return GITHUB; if (CI_PROJECT_URL) return GITLAB; @@ -105,7 +106,7 @@ class CML { const watermark = rmWatermark ? '' - : `![CML watermark](https://raw.githubusercontent.com/iterative/cml/master/assets/watermark.svg)`; + : '![CML watermark](https://raw.githubusercontent.com/iterative/cml/master/assets/watermark.svg)'; const report = `${userReport}\n\n${watermark}`; const drv = getDriver(this);