Skip to content

Commit 17a045e

Browse files
Domas Monkuscasperdcl0x2b3bfa0dacbd
authored
Augment watermark with label (#1173)
* Augment watermark with label. The label may contain special strings `{workflow}` or `{run}` as an easy way to make updatable comments specific to the workflow or execution. * Check for presence of a node title. * Specify conflict between commentLabel and rmWatermark flags. * Update bin/cml/comment/create.js Co-authored-by: Casper da Costa-Luis <casper.dcl@physics.org> * Update cli flag for comment label. * Rename --label to --id * Rename parameter to commentTag. * Update src/cml.js Co-authored-by: Helio Machado <0x2b3bfa0+git@googlemail.com> * Rename commentTag flag to watermarkTitle. * Update bin/cml/comment/create.js Co-authored-by: Casper da Costa-Luis <casper.dcl@physics.org> * Update snapshots. * Refactor watermark function. * Fix test. * Use markdown-escape to escape markdown control characters in comment watermark titles. * Revert "Use markdown-escape to escape markdown control characters in comment watermark titles." This reverts commit 1400685. * Simplify escaping of markdown control characters. It appears that github only escapes asterisks and underscores. * Add more patterns to escape. * Run replacement on title, not full watermark. Co-authored-by: Casper da Costa-Luis <casper.dcl@physics.org> Co-authored-by: Helio Machado <0x2b3bfa0+git@googlemail.com> Co-authored-by: Daniel Barnes <dabarnes2b@gmail.com>
1 parent 329ed42 commit 17a045e

File tree

7 files changed

+92
-21
lines changed

7 files changed

+92
-21
lines changed

bin/cml/comment/create.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ exports.options = kebabcaseKeys({
6868
'Avoid watermark; CML needs a watermark to be able to distinguish CML comments from others',
6969
hidden: true,
7070
telemetryData: 'name'
71+
},
72+
watermarkTitle: {
73+
type: 'string',
74+
description:
75+
'Hidden comment marker (used for targeting in subsequent `cml comment update`); "{workflow}" & "{run}" are auto-replaced',
76+
default: '',
77+
conflicts: ['rmWatermark']
7178
}
7279
});
7380
exports.DOCSURL = DOCSURL;

bin/cml/comment/create.test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const { exec } = require('../../../src/utils');
33
describe('Comment integration tests', () => {
44
test('cml send-comment --help', async () => {
55
const output = await exec(`node ./bin/cml.js send-comment --help`);
6-
76
expect(output).toMatchInlineSnapshot(`
87
"cml.js send-comment <markdown file>
98
@@ -30,7 +29,10 @@ describe('Comment integration tests', () => {
3029
[string] [default: \\"https://asset.cml.dev\\"]
3130
--publish-native, --native Uses driver's native capabilities to upload assets
3231
instead of CML's storage; not available on GitHub
33-
[boolean]"
32+
[boolean]
33+
--watermark-title Hidden comment marker (used for targeting in
34+
subsequent \`cml comment update\`); \\"{workflow}\\" &
35+
\\"{run}\\" are auto-replaced [string] [default: \\"\\"]"
3436
`);
3537
});
3638
});

src/cml.js

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const {
2323

2424
const { GITHUB_REPOSITORY, CI_PROJECT_URL, BITBUCKET_REPO_UUID } = process.env;
2525

26+
const WATERMARK_IMAGE = 'https://cml.dev/watermark.png';
2627
const GIT_USER_NAME = 'Olivaw[bot]';
2728
const GIT_USER_EMAIL = 'olivaw@iterative.ai';
2829
const GIT_REMOTE = 'origin';
@@ -164,26 +165,55 @@ class CML {
164165
const triggerSha = await this.triggerSha();
165166
const {
166167
commitSha: inCommitSha = triggerSha,
167-
rmWatermark,
168-
update,
168+
markdownFile,
169169
pr,
170170
publish,
171171
publishUrl,
172-
markdownFile,
173172
report: testReport,
173+
rmWatermark,
174+
triggerFile,
175+
update,
174176
watch,
175-
triggerFile
177+
watermarkTitle
176178
} = opts;
177179

180+
const drv = this.getDriver();
181+
178182
const commitSha =
179183
(await this.revParse({ ref: inCommitSha })) || inCommitSha;
180184

181185
if (rmWatermark && update)
182186
throw new Error('watermarks are mandatory for updateable comments');
183187

188+
// Create the watermark.
189+
const genWatermark = (opts = {}) => {
190+
const { label = '', workflow, run } = opts;
191+
// Replace {workflow} and {run} placeholders in label with actual values.
192+
const lbl = label.replace('{workflow}', workflow).replace('{run}', run);
193+
194+
let title = `CML watermark ${lbl}`.trim();
195+
// Github appears to escape underscores and asterisks in markdown content.
196+
// Without escaping them, the watermark content in comments retrieved
197+
// from github will not match the input.
198+
const patterns = [
199+
[/_/g, '\\_'], // underscore
200+
[/\*/g, '\\*'], // asterisk
201+
[/\[/g, '\\['], // opening square bracket
202+
[/</g, '\\<'] // opening angle bracket
203+
];
204+
title = patterns.reduce(
205+
(label, pattern) => label.replace(pattern[0], pattern[1]),
206+
title
207+
);
208+
return `![](${WATERMARK_IMAGE} "${title}")`;
209+
};
184210
const watermark = rmWatermark
185211
? ''
186-
: '![CML watermark](https://raw.githubusercontent.com/iterative/cml/master/assets/watermark.svg)';
212+
: genWatermark({
213+
label: watermarkTitle,
214+
workflow: drv.workflowId,
215+
run: drv.runId
216+
});
187217

188218
let userReport = testReport;
189219
try {
@@ -195,15 +225,17 @@ class CML {
195225
}
196226

197227
let report = `${userReport}\n\n${watermark}`;
198-
const drv = this.getDriver();
199228

200229
const publishLocalFiles = async (tree) => {
201230
const nodes = [];
202231

203232
visit(tree, ['definition', 'image', 'link'], (node) => nodes.push(node));
204233

234+
const isWatermark = (node) => {
235+
return node.title && node.title.startsWith('CML watermark');
236+
};
205237
const visitor = async (node) => {
206-
if (node.url && node.alt !== 'CML watermark') {
238+
if (node.url && !isWatermark(node)) {
207239
const absolutePath = path.resolve(
208240
path.dirname(markdownFile),
209241
node.url
@@ -264,7 +296,7 @@ class CML {
264296
let comment;
265297
const updatableComment = (comments) => {
266298
return comments.reverse().find(({ body }) => {
267-
return body.includes('watermark.svg');
299+
return body.includes(watermark);
268300
});
269301
};
270302

src/drivers/bitbucket_cloud.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ const ProxyAgent = require('proxy-agent');
88

99
const { fetchUploadData, exec, gpuPresent, sleep } = require('../utils');
1010

11-
const { BITBUCKET_COMMIT, BITBUCKET_BRANCH, BITBUCKET_PIPELINE_UUID } =
12-
process.env;
11+
const {
12+
BITBUCKET_COMMIT,
13+
BITBUCKET_BRANCH,
14+
BITBUCKET_PIPELINE_UUID,
15+
BITBUCKET_BUILD_NUMBER
16+
} = process.env;
1317

1418
class BitbucketCloud {
1519
constructor(opts = {}) {
@@ -421,6 +425,14 @@ class BitbucketCloud {
421425
return command;
422426
}
423427

428+
get workflowId() {
429+
return BITBUCKET_PIPELINE_UUID;
430+
}
431+
432+
get runId() {
433+
return BITBUCKET_BUILD_NUMBER;
434+
}
435+
424436
get sha() {
425437
return BITBUCKET_COMMIT;
426438
}

src/drivers/github.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ const CHECK_TITLE = 'CML Report';
1818
process.env.RUNNER_ALLOW_RUNASROOT = 1;
1919

2020
const {
21-
GITHUB_REPOSITORY,
22-
GITHUB_SHA,
23-
GITHUB_REF,
24-
GITHUB_HEAD_REF,
21+
CI,
2522
GITHUB_EVENT_NAME,
23+
GITHUB_HEAD_REF,
24+
GITHUB_REF,
25+
GITHUB_REPOSITORY,
2626
GITHUB_RUN_ID,
27+
GITHUB_SHA,
2728
GITHUB_TOKEN,
28-
CI,
29+
GITHUB_WORKFLOW,
2930
TPI_TASK
3031
} = process.env;
3132

@@ -705,6 +706,14 @@ class Github {
705706
return command;
706707
}
707708

709+
get workflowId() {
710+
return GITHUB_WORKFLOW;
711+
}
712+
713+
get runId() {
714+
return GITHUB_RUN_ID;
715+
}
716+
708717
warn(message) {
709718
console.error(`::warning::${message}`);
710719
}

src/drivers/gitlab.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const winston = require('winston');
1111

1212
const { fetchUploadData, download, gpuPresent } = require('../utils');
1313

14-
const { IN_DOCKER, CI_PIPELINE_ID } = process.env;
14+
const { CI_JOB_ID, CI_PIPELINE_ID, IN_DOCKER } = process.env;
15+
1516
const API_VER = 'v4';
1617
class Gitlab {
1718
constructor(opts = {}) {
@@ -444,6 +445,14 @@ class Gitlab {
444445
return command;
445446
}
446447

448+
get workflowId() {
449+
return CI_PIPELINE_ID;
450+
}
451+
452+
get runId() {
453+
return CI_JOB_ID;
454+
}
455+
447456
get sha() {
448457
return process.env.CI_COMMIT_SHA;
449458
}

src/utils.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,14 @@ const isProcRunning = async (opts) => {
136136
};
137137

138138
const watermarkUri = ({ uri, type } = {}) => {
139-
return uriParmam({ uri, param: 'cml', value: type });
139+
return uriParam({ uri, param: 'cml', value: type });
140140
};
141141

142142
const preventcacheUri = ({ uri } = {}) => {
143-
return uriParmam({ uri, param: 'cache-bypass', value: uuid.v4() });
143+
return uriParam({ uri, param: 'cache-bypass', value: uuid.v4() });
144144
};
145145

146-
const uriParmam = (opts = {}) => {
146+
const uriParam = (opts = {}) => {
147147
const { uri, param, value } = opts;
148148
const url = new URL(uri);
149149
url.searchParams.set(param, value);

0 commit comments

Comments
 (0)