Skip to content

Commit 7973d0e

Browse files
optional telemetry (#1092)
* Telemetry * appdirs * analytics tested * remove analytics * deterministic check * remove fake code * fix isCI * cml * integrated * endpoint * defensive * json spec * Telemetry (#1092) suggestion: don't repeat ourselves (#1094) * Telemetry (#1092) suggestion: don't repeat ourselves * Fix manual merging mistakes * Obliterate the last process.exit on business logic * dryer cml * telemetrySend * naming consistency * read non json id * gh check repoOptions * one error handler * fix tensorboard * fix tensorboard and tests * cleanup yargs fail handler * unused * RUNNER_SHUTTING_DOWN * remove comments * golantic * getDriver * fix telemetry * fix getter * remove report * remove console * fix os release * windows release Co-authored-by: Helio Machado <[email protected]>
1 parent f8584cc commit 7973d0e

24 files changed

+1650
-3453
lines changed

Diff for: bin/cml.js

+67-42
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,39 @@ const which = require('which');
77
const winston = require('winston');
88
const yargs = require('yargs');
99

10-
const configureLogger = (level) => {
10+
const CML = require('../src/cml').default;
11+
const { jitsuEventPayload } = require('../src/analytics');
12+
let OPTS;
13+
14+
const setupOpts = (opts) => {
15+
const legacyEnvironmentVariables = {
16+
TB_CREDENTIALS: 'CML_TENSORBOARD_DEV_CREDENTIALS',
17+
DOCKER_MACHINE: 'CML_RUNNER_DOCKER_MACHINE',
18+
RUNNER_IDLE_TIMEOUT: 'CML_RUNNER_IDLE_TIMEOUT',
19+
RUNNER_LABELS: 'CML_RUNNER_LABELS',
20+
RUNNER_SINGLE: 'CML_RUNNER_SINGLE',
21+
RUNNER_REUSE: 'CML_RUNNER_REUSE',
22+
RUNNER_NO_RETRY: 'CML_RUNNER_NO_RETRY',
23+
RUNNER_DRIVER: 'CML_RUNNER_DRIVER',
24+
RUNNER_REPO: 'CML_RUNNER_REPO',
25+
RUNNER_PATH: 'CML_RUNNER_PATH'
26+
};
27+
28+
for (const [oldName, newName] of Object.entries(legacyEnvironmentVariables)) {
29+
if (process.env[oldName]) process.env[newName] = process.env[oldName];
30+
}
31+
32+
const { markdownfile } = opts;
33+
opts.markdownFile = markdownfile;
34+
opts.cmlCommand = opts._[0];
35+
opts.cml = new CML(opts);
36+
37+
OPTS = opts;
38+
};
39+
40+
const setupLogger = (opts) => {
41+
const { log: level } = opts;
42+
1143
winston.configure({
1244
format: process.stdout.isTTY
1345
? winston.format.combine(
@@ -29,59 +61,52 @@ const configureLogger = (level) => {
2961
});
3062
};
3163

64+
const setupTelemetry = async (opts) => {
65+
const { cml, cmlCommand: action } = opts;
66+
opts.telemetryEvent = await jitsuEventPayload({ action, cml });
67+
};
68+
3269
const runPlugin = async ({ $0: executable, command }) => {
33-
try {
34-
if (command === undefined) throw new Error('no command');
35-
const path = which.sync(`${basename(executable)}-${command}`);
36-
const parameters = process.argv.slice(process.argv.indexOf(command) + 1); // HACK
37-
process.exit(await pseudoexec(path, parameters));
38-
} catch (error) {
39-
yargs.showHelp();
40-
winston.debug(error);
41-
}
70+
if (command === undefined) throw new Error('no command');
71+
const path = which.sync(`${basename(executable)}-${command}`);
72+
const parameters = process.argv.slice(process.argv.indexOf(command) + 1); // HACK
73+
await pseudoexec(path, parameters);
4274
};
4375

44-
const handleError = (message, error) => {
76+
const handleError = async (message, error, yargs) => {
4577
if (error) {
46-
winston.error(error);
47-
} else {
48-
yargs.showHelp();
49-
console.error('\n' + message);
78+
const { telemetryEvent, cml } = OPTS;
79+
const event = { ...telemetryEvent, error: error.message };
80+
await cml.telemetrySend({ event });
81+
return;
5082
}
51-
process.exit(1);
52-
};
5383

54-
const options = {
55-
log: {
56-
type: 'string',
57-
description: 'Maximum log level',
58-
coerce: (value) => configureLogger(value) && value,
59-
choices: ['error', 'warn', 'info', 'debug'],
60-
default: 'info'
61-
}
84+
yargs.showHelp();
85+
console.error('\n' + message);
6286
};
6387

64-
const legacyEnvironmentVariables = {
65-
TB_CREDENTIALS: 'CML_TENSORBOARD_DEV_CREDENTIALS',
66-
DOCKER_MACHINE: 'CML_RUNNER_DOCKER_MACHINE',
67-
RUNNER_IDLE_TIMEOUT: 'CML_RUNNER_IDLE_TIMEOUT',
68-
RUNNER_LABELS: 'CML_RUNNER_LABELS',
69-
RUNNER_SINGLE: 'CML_RUNNER_SINGLE',
70-
RUNNER_REUSE: 'CML_RUNNER_REUSE',
71-
RUNNER_NO_RETRY: 'CML_RUNNER_NO_RETRY',
72-
RUNNER_DRIVER: 'CML_RUNNER_DRIVER',
73-
RUNNER_REPO: 'CML_RUNNER_REPO',
74-
RUNNER_PATH: 'CML_RUNNER_PATH'
75-
};
88+
process.on('uncaughtException', async (err) => {
89+
await handleError('', err, yargs);
90+
});
7691

77-
for (const [oldName, newName] of Object.entries(legacyEnvironmentVariables)) {
78-
if (process.env[oldName]) process.env[newName] = process.env[oldName];
79-
}
92+
process.on('unhandledRejection', async (reason) => {
93+
await handleError('', new Error(reason), yargs);
94+
});
8095

8196
yargs
82-
.fail(handleError)
8397
.env('CML')
84-
.options(options)
98+
.options({
99+
log: {
100+
type: 'string',
101+
description: 'Maximum log level',
102+
choices: ['error', 'warn', 'info', 'debug'],
103+
default: 'info'
104+
}
105+
})
106+
.fail(handleError)
107+
.middleware(setupOpts)
108+
.middleware(setupLogger)
109+
.middleware(setupTelemetry)
85110
.commandDir('./cml', { exclude: /\.test\.js$/ })
86111
.command('$0 <command>', false, (builder) => builder.strict(false), runPlugin)
87112
.recommendCommands()

Diff for: bin/cml/ci.js

+5-19
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
const kebabcaseKeys = require('kebabcase-keys');
22

3-
const { GIT_USER_NAME, GIT_USER_EMAIL } = require('../../src/cml');
4-
const CML = require('../../src/cml').default;
3+
const { GIT_USER_NAME, GIT_USER_EMAIL, repoOptions } = require('../../src/cml');
54

65
exports.command = 'ci';
76
exports.description = 'Fixes specific CI setups';
87

98
exports.handler = async (opts) => {
10-
const cml = new CML(opts);
11-
console.log((await cml.ci(opts)) || '');
9+
const { cml, telemetryEvent: event } = opts;
10+
await cml.ci(opts);
11+
await cml.telemetrySend({ event });
1212
};
1313

1414
exports.builder = (yargs) =>
1515
yargs.env('CML_CI').options(
1616
kebabcaseKeys({
17+
...repoOptions,
1718
unshallow: {
1819
type: 'boolean',
1920
description:
@@ -28,21 +29,6 @@ exports.builder = (yargs) =>
2829
type: 'string',
2930
default: GIT_USER_NAME,
3031
description: 'Set Git user name.'
31-
},
32-
repo: {
33-
type: 'string',
34-
description:
35-
'Set repository to be used. If unspecified, inferred from the environment.'
36-
},
37-
token: {
38-
type: 'string',
39-
description:
40-
'Personal access token to be used. If unspecified, inferred from the environment.'
41-
},
42-
driver: {
43-
type: 'string',
44-
choices: ['github', 'gitlab', 'bitbucket'],
45-
description: 'If unspecified, inferred from the environment.'
4632
}
4733
})
4834
);

Diff for: bin/cml/ci.test.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ describe('CML e2e', () => {
1414
--version Show version number [boolean]
1515
--log Maximum log level
1616
[string] [choices: \\"error\\", \\"warn\\", \\"info\\", \\"debug\\"] [default: \\"info\\"]
17+
--repo Specifies the repo to be used. If not specified is extracted
18+
from the CI ENV. [string]
19+
--token Personal access token to be used. If not specified is extracted
20+
from ENV REPO_TOKEN. [string]
21+
--driver If not specify it infers it from the ENV.
22+
[string] [choices: \\"github\\", \\"gitlab\\", \\"bitbucket\\"]
1723
--unshallow Fetch as much as possible, converting a shallow repository to a
1824
complete one. [boolean]
1925
--user-email Set Git user email. [string] [default: \\"[email protected]\\"]
20-
--user-name Set Git user name. [string] [default: \\"Olivaw[bot]\\"]
21-
--repo Set repository to be used. If unspecified, inferred from the
22-
environment. [string]
23-
--token Personal access token to be used. If unspecified, inferred from
24-
the environment. [string]
25-
--driver If unspecified, inferred from the environment.
26-
[string] [choices: \\"github\\", \\"gitlab\\", \\"bitbucket\\"]"
26+
--user-name Set Git user name. [string] [default: \\"Olivaw[bot]\\"]"
2727
`);
2828
});
2929
});

Diff for: bin/cml/pr.js

+11-20
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
const kebabcaseKeys = require('kebabcase-keys');
22

3-
const { GIT_REMOTE, GIT_USER_NAME, GIT_USER_EMAIL } = require('../../src/cml');
4-
const CML = require('../../src/cml').default;
3+
const {
4+
GIT_REMOTE,
5+
GIT_USER_NAME,
6+
GIT_USER_EMAIL,
7+
repoOptions
8+
} = require('../../src/cml');
59

610
exports.command = 'pr <glob path...>';
711
exports.description = 'Create a pull request with the specified files';
812

913
exports.handler = async (opts) => {
10-
const cml = new CML(opts);
11-
const link = await cml.prCreate({ ...opts, globs: opts.globpath });
12-
if (link) console.log(link);
14+
const { cml, telemetryEvent: event, globpath: globs } = opts;
15+
const link = await cml.prCreate({ ...opts, globs });
16+
console.log(link);
17+
await cml.telemetrySend({ event });
1318
};
1419

1520
exports.builder = (yargs) =>
1621
yargs.env('CML_PR').options(
1722
kebabcaseKeys({
23+
...repoOptions,
1824
md: {
1925
type: 'boolean',
2026
description: 'Output in markdown format [](url).'
@@ -69,21 +75,6 @@ exports.builder = (yargs) =>
6975
type: 'string',
7076
default: GIT_USER_NAME,
7177
description: 'Sets git user name.'
72-
},
73-
repo: {
74-
type: 'string',
75-
description:
76-
'Specifies the repo to be used. If not specified is extracted from the CI ENV.'
77-
},
78-
token: {
79-
type: 'string',
80-
description:
81-
'Personal access token to be used. If not specified in extracted from ENV REPO_TOKEN.'
82-
},
83-
driver: {
84-
type: 'string',
85-
choices: ['github', 'gitlab', 'bitbucket'],
86-
description: 'If not specify it infers it from the ENV.'
8778
}
8879
})
8980
);

Diff for: bin/cml/pr.test.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ describe('CML e2e', () => {
1414
--version Show version number [boolean]
1515
--log Maximum log level
1616
[string] [choices: \\"error\\", \\"warn\\", \\"info\\", \\"debug\\"] [default: \\"info\\"]
17+
--repo Specifies the repo to be used. If not specified is
18+
extracted from the CI ENV. [string]
19+
--token Personal access token to be used. If not specified is
20+
extracted from ENV REPO_TOKEN. [string]
21+
--driver If not specify it infers it from the ENV.
22+
[string] [choices: \\"github\\", \\"gitlab\\", \\"bitbucket\\"]
1723
--md Output in markdown format [](url). [boolean]
1824
--skip-ci Force skip CI for the created commit (if any) [boolean]
1925
--merge, --auto-merge Try to merge the pull request upon creation. [boolean]
@@ -28,13 +34,7 @@ describe('CML e2e', () => {
2834
--remote Sets git remote. [string] [default: \\"origin\\"]
2935
--user-email Sets git user email.
3036
[string] [default: \\"[email protected]\\"]
31-
--user-name Sets git user name. [string] [default: \\"Olivaw[bot]\\"]
32-
--repo Specifies the repo to be used. If not specified is
33-
extracted from the CI ENV. [string]
34-
--token Personal access token to be used. If not specified in
35-
extracted from ENV REPO_TOKEN. [string]
36-
--driver If not specify it infers it from the ENV.
37-
[string] [choices: \\"github\\", \\"gitlab\\", \\"bitbucket\\"]"
37+
--user-name Sets git user name. [string] [default: \\"Olivaw[bot]\\"]"
3838
`);
3939
});
4040
});

Diff for: bin/cml/publish.js

+6-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const fs = require('fs').promises;
22
const kebabcaseKeys = require('kebabcase-keys');
33
const winston = require('winston');
44

5-
const CML = require('../../src/cml').default;
5+
const { CML, repoOptions } = require('../../src/cml');
66

77
exports.command = 'publish <asset>';
88
exports.description = 'Upload an image to build a report';
@@ -15,23 +15,20 @@ exports.handler = async (opts) => {
1515
opts.native = true;
1616
}
1717

18-
const { file, repo, native } = opts;
19-
20-
const path = opts.asset;
18+
const { file, repo, native, asset: path, telemetryEvent: event } = opts;
2119
const cml = new CML({ ...opts, repo: native ? repo : 'cml' });
22-
23-
const output = await cml.publish({
24-
...opts,
25-
path
26-
});
20+
const output = await cml.publish({ ...opts, path });
2721

2822
if (!file) console.log(output);
2923
else await fs.writeFile(file, output);
24+
25+
await cml.telemetrySend({ event });
3026
};
3127

3228
exports.builder = (yargs) =>
3329
yargs.env('CML_PUBLISH').options(
3430
kebabcaseKeys({
31+
...repoOptions,
3532
url: {
3633
type: 'string',
3734
description: 'Self-Hosted URL',
@@ -75,16 +72,6 @@ exports.builder = (yargs) =>
7572
type: 'string',
7673
description:
7774
'Specifies the repo to be used. If not specified is extracted from the CI ENV.'
78-
},
79-
token: {
80-
type: 'string',
81-
description:
82-
'Personal access token to be used. If not specified, extracted from ENV REPO_TOKEN, GITLAB_TOKEN, GITHUB_TOKEN, or BITBUCKET_TOKEN.'
83-
},
84-
driver: {
85-
type: 'string',
86-
choices: ['github', 'gitlab', 'bitbucket'],
87-
description: 'If not specify it infers it from the ENV.'
8875
}
8976
})
9077
);

Diff for: bin/cml/publish.test.js

+7-8
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,19 @@ describe('CML e2e', () => {
1515
--version Show version number [boolean]
1616
--log Maximum log level
1717
[string] [choices: \\"error\\", \\"warn\\", \\"info\\", \\"debug\\"] [default: \\"info\\"]
18+
--repo Specifies the repo to be used. If not specified is
19+
extracted from the CI ENV. [string]
20+
--token Personal access token to be used. If not specified is
21+
extracted from ENV REPO_TOKEN. [string]
22+
--driver If not specify it infers it from the ENV.
23+
[string] [choices: \\"github\\", \\"gitlab\\", \\"bitbucket\\"]
1824
--md Output in markdown format [title || name](url). [boolean]
1925
-t, --title Markdown title [title](url) or ![](url title). [string]
2026
--native Uses driver's native capabilities to upload assets instead
2127
of CML's storage. Not available on GitHub. [boolean]
2228
--rm-watermark Avoid CML watermark. [boolean]
2329
--mime-type Specifies the mime-type. If not set guess it from the
24-
content. [string]
25-
--repo Specifies the repo to be used. If not specified is
26-
extracted from the CI ENV. [string]
27-
--token Personal access token to be used. If not specified,
28-
extracted from ENV REPO_TOKEN, GITLAB_TOKEN, GITHUB_TOKEN,
29-
or BITBUCKET_TOKEN. [string]
30-
--driver If not specify it infers it from the ENV.
31-
[string] [choices: \\"github\\", \\"gitlab\\", \\"bitbucket\\"]"
30+
content. [string]"
3231
`);
3332
});
3433

0 commit comments

Comments
 (0)