Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit b950a04

Browse files
authored
Merge pull request #164 from microsoft/cli-update-check-refactor
Check for version updates no more than 1x per day, based on timestamp in config
2 parents 7f51ce5 + 4040f6c commit b950a04

File tree

4 files changed

+112
-23
lines changed

4 files changed

+112
-23
lines changed

packages/cli/scripts/postinstall.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const getUserConfig = async () => {
3636
const promptTelemetry = async () => {
3737
try {
3838
const userConfig = await getUserConfig()
39+
userConfig.lastVersionCheck = new Date()
3940
if (userConfig.telemetry === null) {
4041
const disableTelemetry = await cli.prompt(chalk.red('Telemetry is disabled. Would you like to opt in? Only command and flags usage will be sent. (Y/N)'))
4142
if (disableTelemetry === 'Y' || disableTelemetry === 'y') {
@@ -53,7 +54,6 @@ const getUserConfig = async () => {
5354
}
5455

5556
await fs.mkdirp(pathToConfigJson)
56-
5757
await fs.writeFile(path.join(pathToConfigJson, 'config.json'), JSON.stringify(userConfig, null, 2))
5858
}
5959
/* tslint:disable:no-unused */

packages/cli/src/hooks/init/inithook.ts

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,64 @@ const latestVersion = require('latest-version')
77
const semver = require('semver')
88

99
const hook: Hook<'init'> = async function (opts) {
10-
// Look for updates
11-
try {
12-
const latest = await latestVersion(opts.config.name, {version: `>${opts.config.version}`})
13-
if (semver.gt(latest, opts.config.version)) {
14-
this.log('Update available ')
15-
this.log(' Run ')
16-
this.log(`npm i -g ${opts.config.name} `)
17-
}
18-
/* tslint:disable:no-unused */
19-
} catch (err) {
20-
// swallow the exception; we don't want to crash the app
21-
// on a failed attempt to check version
10+
// get config settings
11+
let userConfig: any
12+
const curDateTime = new Date()
13+
const configFileExists = fs.existsSync(path.join(this.config.configDir, 'config.json'))
14+
15+
const writeUserConfig = async (userconfig: any) => {
16+
await fs.mkdirp(this.config.configDir)
17+
await fs.writeFile(path.join(this.config.configDir, 'config.json'), JSON.stringify(userconfig, null, 2))
2218
}
2319

24-
// Ensure telemetry is set
2520
try {
26-
let userConfig: any = ''
27-
if (fs.existsSync(path.join(this.config.configDir, 'config.json'))) {
21+
// if config file exists, load settings
22+
if (configFileExists) {
2823
userConfig = await fs.readJSON(path.join(this.config.configDir, 'config.json'))
2924
} else {
25+
// otherwise create in-memory config
3026
userConfig = {
3127
telemetry: null,
28+
lastVersionCheck: null
29+
}
30+
}
31+
32+
const checkForUpdate = async () => {
33+
const latest = await latestVersion(opts.config.name, {version: `>${opts.config.version}`})
34+
if (semver.gt(latest, opts.config.version)) {
35+
this.log('Update available ')
36+
this.log(' Run ')
37+
this.log(`npm i -g ${opts.config.name} `)
3238
}
3339
}
3440

41+
const updateUserConfig = async (curVersionCheck: Date) => {
42+
userConfig.lastVersionCheck = curVersionCheck
43+
await writeUserConfig(userConfig)
44+
}
45+
46+
const isToday = (dateObj: Date | null, today: Date) => {
47+
return dateObj && dateObj.getDate() === today.getDate() &&
48+
dateObj.getMonth() === today.getMonth() &&
49+
dateObj.getFullYear() === today.getFullYear()
50+
}
51+
52+
// if there's no timestamp in config, create one and check for updates
53+
// if there is a timestamp in config and it's not from today, check for updates
54+
const lastCheck = userConfig.lastVersionCheck ? new Date(userConfig.lastVersionCheck) : null
55+
if (!isToday(lastCheck, curDateTime)) {
56+
await checkForUpdate()
57+
await updateUserConfig(curDateTime)
58+
}
59+
60+
/* tslint:disable:no-unused */
61+
} catch (err) {
62+
// swallow the exception; we don't want to crash the app
63+
// on a failed attempt to check version
64+
}
65+
66+
// Ensure telemetry is set
67+
try {
3568
if (userConfig.telemetry === null) {
3669
const disableTelemetry = await cli.prompt(chalk.red('Telemetry is disabled. Would you like to opt in?. Only command and flags usage will be sent. (Y/N)'))
3770
if (disableTelemetry === 'Y' || disableTelemetry === 'y') {
@@ -48,9 +81,7 @@ const hook: Hook<'init'> = async function (opts) {
4881
this.log(chalk.blue('bf config:telemetry:enable'))
4982
}
5083

51-
await fs.mkdirp(this.config.configDir)
52-
53-
await fs.writeFileSync(path.join(this.config.configDir, 'config.json'), JSON.stringify(userConfig, null, 2))
84+
await writeUserConfig(userConfig)
5485
}
5586

5687
this.config.pjson.telemetry = userConfig.telemetry

packages/cli/test/commands/inithook.test.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const path = require('path')
55
const semver = require('semver')
66
const os = require('os')
77

8+
const lastversioncheck = path.join(__dirname, '../fixtures/lastversioncheck')
89
const upgradeavailable = path.join(__dirname, '../fixtures/upgradeavailable')
910
const rootTelemetryNull = path.join(__dirname, '../fixtures/telemetrynull')
1011

@@ -89,13 +90,13 @@ describe('Check if telemetry is not changed if config is not null', () => {
8990
})
9091

9192
describe('Update available to stdout', () => {
92-
before(function() {
93+
beforeEach(function() {
9394
// runs before all tests in this block
9495
fs.mkdirSync(pathToConfigJsonUpdate)
95-
fs.writeFileSync(path.join(pathToConfigJsonUpdate, 'config.json'), JSON.stringify({telemetry: true,}, null, 2))
96+
fs.writeFileSync(path.join(pathToConfigJsonUpdate, 'config.json'), JSON.stringify({telemetry: true}, null, 2))
9697
});
9798

98-
after(function() {
99+
afterEach(function() {
99100
// runs after all tests in this block
100101
fs.removeSync(pathToConfigJsonUpdate)
101102
});
@@ -110,11 +111,50 @@ describe('Update available to stdout', () => {
110111
})
111112
.it('it should output to stdout if update is available')
112113

113-
test
114+
test
114115
.loadConfig({root: upgradeavailable})
115116
.stub(semver, 'gt', () => false)
116117
.stdout()
117118
.hook('init', {argv: ['arg']}, {root: upgradeavailable})
118119
.do(output => expect(output.stdout).to.equal(''))
119120
.it('it should not output anything if no update is available')
121+
122+
test
123+
.loadConfig({root: lastversioncheck})
124+
.stub(semver, 'gt', () => true)
125+
.stdout()
126+
.hook('init', {argv: ['arg']}, {root: lastversioncheck})
127+
.do(output => {
128+
expect(output.stdout).to.contain('Update available')
129+
})
130+
.it('it should write to message stdout if update is available and last checked timestamp is expired')
131+
120132
})
133+
134+
describe('bypass version update if it\'s already been checked today', async () => {
135+
beforeEach(async () => {
136+
// runs before all tests in this block
137+
const today = new Date()
138+
let userConfig = await fs.readJSON(path.join(lastversioncheck, 'package.json'))
139+
userConfig.lastversioncheck = today
140+
fs.writeFileSync(path.join(lastversioncheck, 'package.json'), JSON.stringify(userConfig, null, 2))
141+
fs.mkdirSync(pathToConfigJsonUpdate)
142+
fs.writeFileSync(path.join(pathToConfigJsonUpdate, 'config.json'), JSON.stringify({telemetry: true}, null, 2))
143+
});
144+
145+
afterEach(function() {
146+
// runs after all tests in this block
147+
fs.removeSync(pathToConfigJsonUpdate)
148+
});
149+
150+
test
151+
.loadConfig({root: lastversioncheck})
152+
.stub(semver, 'gt', () => true)
153+
.stdout()
154+
.hook('init', {argv: ['arg']}, {root: lastversioncheck})
155+
.do(output => {
156+
expect(output.stdout).to.contain('')
157+
})
158+
.it('it should not output anything if last upgrade check was today')
159+
160+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "botbuilder",
3+
"version": "1.0.0",
4+
"lastVersionCheck": "2019-09-23T19:27:24.987Z",
5+
"files": [],
6+
"oclif": {
7+
"commands": "../../../src/commands",
8+
"bin": "bf",
9+
"plugins": [
10+
"@oclif/plugin-help",
11+
"@microsoft/bf-chatdown"
12+
],
13+
"hooks": {
14+
"init": "../../../src/hooks/init/inithook"
15+
}
16+
},
17+
"lastversioncheck": "2019-09-27T23:36:07.377Z"
18+
}

0 commit comments

Comments
 (0)