diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e30c7..2472fe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.28.0](https://github.com/rpidanny/darwin/compare/v1.27.0...v1.28.0) (2024-07-03) + +### Features + +* add check-update hook ([#71](https://github.com/rpidanny/darwin/issues/71)) ([8ad8e33](https://github.com/rpidanny/darwin/commit/8ad8e3356b2d615612d9407bc56f9aba47c1ff77)) + ## [1.27.0](https://github.com/rpidanny/darwin/compare/v1.26.1...v1.27.0) (2024-07-03) ### Features diff --git a/README.md b/README.md index c3e1e2a..6643174 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ $ npm install -g @rpidanny/darwin $ darwin COMMAND running command... $ darwin (--version) -@rpidanny/darwin/1.27.0 linux-x64 node-v20.15.0 +@rpidanny/darwin/1.28.0 linux-x64 node-v20.15.0 $ darwin --help [COMMAND] USAGE $ darwin COMMAND diff --git a/docs/chat.md b/docs/chat.md index b09268f..2c813f4 100644 --- a/docs/chat.md +++ b/docs/chat.md @@ -32,4 +32,4 @@ EXAMPLES $ darwin chat ``` -_See code: [src/commands/chat/index.ts](https://github.com/rpidanny/darwin/blob/v1.27.0/src/commands/chat/index.ts)_ +_See code: [src/commands/chat/index.ts](https://github.com/rpidanny/darwin/blob/v1.28.0/src/commands/chat/index.ts)_ diff --git a/docs/config.md b/docs/config.md index c433fd9..395e9a9 100644 --- a/docs/config.md +++ b/docs/config.md @@ -25,7 +25,7 @@ EXAMPLES $ darwin config get ``` -_See code: [src/commands/config/get.ts](https://github.com/rpidanny/darwin/blob/v1.27.0/src/commands/config/get.ts)_ +_See code: [src/commands/config/get.ts](https://github.com/rpidanny/darwin/blob/v1.28.0/src/commands/config/get.ts)_ ## `darwin config set` @@ -42,4 +42,4 @@ EXAMPLES $ darwin config set ``` -_See code: [src/commands/config/set.ts](https://github.com/rpidanny/darwin/blob/v1.27.0/src/commands/config/set.ts)_ +_See code: [src/commands/config/set.ts](https://github.com/rpidanny/darwin/blob/v1.28.0/src/commands/config/set.ts)_ diff --git a/docs/download.md b/docs/download.md index 568de37..ba6e6ac 100644 --- a/docs/download.md +++ b/docs/download.md @@ -32,4 +32,4 @@ EXAMPLES $ darwin download papers "crispr cas9" -o papers/ -c 100 --log-level debug ``` -_See code: [src/commands/download/papers.ts](https://github.com/rpidanny/darwin/blob/v1.27.0/src/commands/download/papers.ts)_ +_See code: [src/commands/download/papers.ts](https://github.com/rpidanny/darwin/blob/v1.28.0/src/commands/download/papers.ts)_ diff --git a/docs/search.md b/docs/search.md index dade419..13f1bbd 100644 --- a/docs/search.md +++ b/docs/search.md @@ -55,7 +55,7 @@ FLAG DESCRIPTIONS Summaries are generated using LLM so make sure LLMs are configured by running `darwin config set` ``` -_See code: [src/commands/search/accession.ts](https://github.com/rpidanny/darwin/blob/v1.27.0/src/commands/search/accession.ts)_ +_See code: [src/commands/search/accession.ts](https://github.com/rpidanny/darwin/blob/v1.28.0/src/commands/search/accession.ts)_ ## `darwin search papers KEYWORDS` @@ -100,4 +100,4 @@ FLAG DESCRIPTIONS Summaries are generated using LLM so make sure LLMs are configured by running `darwin config set` ``` -_See code: [src/commands/search/papers.ts](https://github.com/rpidanny/darwin/blob/v1.27.0/src/commands/search/papers.ts)_ +_See code: [src/commands/search/papers.ts](https://github.com/rpidanny/darwin/blob/v1.28.0/src/commands/search/papers.ts)_ diff --git a/package-lock.json b/package-lock.json index 7f331b3..6ea3d55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rpidanny/darwin", - "version": "1.27.0", + "version": "1.28.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rpidanny/darwin", - "version": "1.27.0", + "version": "1.28.0", "license": "MIT", "dependencies": { "@json2csv/node": "^7.0.6", @@ -39,6 +39,7 @@ "playwright": "^1.44.1", "pretty-ms": "^9.0.0", "reflect-metadata": "^0.2.2", + "semver": "^7.6.2", "tough-cookie": "^4.1.4", "typedi": "^0.10.0" }, @@ -20662,7 +20663,6 @@ "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "devOptional": true, "bin": { "semver": "bin/semver.js" }, diff --git a/package.json b/package.json index 22e7403..d2c81a5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@rpidanny/darwin", "description": "An elegant CLI wizard enhancing biotech research efficiency, with adaptable features for other domains, albeit with minor constraints.", - "version": "1.27.0", + "version": "1.28.0", "author": "Abhishek ", "bin": { "darwin": "./bin/run.js" @@ -61,6 +61,7 @@ "playwright": "^1.44.1", "pretty-ms": "^9.0.0", "reflect-metadata": "^0.2.2", + "semver": "^7.6.2", "tough-cookie": "^4.1.4", "typedi": "^0.10.0" }, @@ -150,7 +151,8 @@ "hooks": { "init": [ "./dist/hooks/init/analytics", - "./dist/hooks/init/banner" + "./dist/hooks/init/banner", + "./dist/hooks/init/check-update" ], "postrun": "./dist/hooks/postrun/analytics" }, diff --git a/src/hooks/init/check-update.spec.ts b/src/hooks/init/check-update.spec.ts new file mode 100644 index 0000000..221c164 --- /dev/null +++ b/src/hooks/init/check-update.spec.ts @@ -0,0 +1,86 @@ +import { jest } from '@jest/globals' +import { Hook } from '@oclif/core' +import nock from 'nock' + +import { getMockConfig } from '../../../test/fixtures/config.js' +import { getMockHooksContext } from '../../../test/fixtures/hooks.js' +import uiOutput from '../../utils/ui/output' +import checkUpdateHook from './check-update.js' + +describe('Hooks - init:check-update', () => { + const latest = '2.1.0' + const current = '1.27.1' + const mockConfig = getMockConfig({ version: current }) + + let hookContext: Hook.Context + + beforeEach(() => { + nock('https://registry.npmjs.org') + .get('/@rpidanny/darwin/latest') + .reply(200, { version: latest }) + + hookContext = getMockHooksContext({ config: mockConfig }) + + jest.spyOn(uiOutput, 'printUpdateBanner').mockImplementation(() => {}) + }) + + afterEach(() => { + jest.resetAllMocks() + jest.clearAllMocks() + }) + + it('should call uiOutput.printUpdateBanner when current version is lower than latest version', async () => { + await checkUpdateHook.call(hookContext, { + id: 'some-command', + argv: [], + config: mockConfig, + context: hookContext, + }) + + expect(uiOutput.printUpdateBanner).toHaveBeenCalledTimes(1) + expect(uiOutput.printUpdateBanner).toHaveBeenCalledWith(latest, hookContext.log) + }) + + it('should not call uiOutput.printUpdateBanner when current version is the latest version', async () => { + const config = getMockConfig({ version: latest }) + const context = getMockHooksContext({ config }) + + await checkUpdateHook.call(context, { + id: 'some-command', + argv: [], + config, + context, + }) + + expect(uiOutput.printUpdateBanner).not.toHaveBeenCalled() + }) + + it.each` + command + ${'autocomplete:script'} + ${'readme'} + `('should not call uiOutput.printUpdateBanner if command is $command', async ({ command }) => { + await checkUpdateHook.call(hookContext, { + id: command, + argv: [], + config: mockConfig, + context: hookContext, + }) + + expect(uiOutput.printUpdateBanner).not.toHaveBeenCalled() + }) + + it('should not throw an error if the request fails', async () => { + nock.cleanAll() + nock('https://registry.npmjs.org').get('/@rpidanny/darwin/latest').reply(500) + + await expect( + checkUpdateHook.call(hookContext, { + id: 'some-command', + argv: [], + config: mockConfig, + context: hookContext, + }), + ).resolves.not.toThrow() + }) +}) diff --git a/src/hooks/init/check-update.ts b/src/hooks/init/check-update.ts new file mode 100644 index 0000000..6c96dae --- /dev/null +++ b/src/hooks/init/check-update.ts @@ -0,0 +1,28 @@ +import { Hook } from '@oclif/core' +import got from 'got' +import semver from 'semver' + +import ui from '../../utils/ui/output.js' + +const hook: Hook<'init'> = async function (opts) { + if (opts.id && ['autocomplete:script', 'readme'].includes(opts.id)) return + + try { + const { version: latestVersion } = await got( + 'https://registry.npmjs.org/@rpidanny/darwin/latest', + { + responseType: 'json', + timeout: 1_000, + }, + ).json<{ version: string }>() + + if (semver.gt(latestVersion, this.config.version)) { + ui.printUpdateBanner(latestVersion, this.log) + } + } catch (error) { + // Silence errors + return + } +} + +export default hook diff --git a/src/utils/ui/output.ts b/src/utils/ui/output.ts index ebf5a06..0f12a31 100644 --- a/src/utils/ui/output.ts +++ b/src/utils/ui/output.ts @@ -11,6 +11,17 @@ function printBanner(version: string, log: (msg: string) => void): void { ) } +function printUpdateBanner(version: string, log: (msg: string) => void): void { + log( + `${chalk.hex('#cf402f')( + `A new version (v${version}) of darwin is available. Please run ${chalk.bold( + 'npm install -g @rpidanny/darwin@latest', + )} to update and enjoy the latest features!`, + )}`, + ) +} + export default { printBanner, + printUpdateBanner, } diff --git a/test/fixtures/config.ts b/test/fixtures/config.ts index e3b47c9..3c4d8e2 100644 --- a/test/fixtures/config.ts +++ b/test/fixtures/config.ts @@ -3,17 +3,18 @@ import path from 'path' import { TConfig } from '../../src/config/schema.js' -export function getMockConfig(): Config { +export function getMockConfig(override?: Partial): Config { const mockConfig = new Config({ root: process.cwd(), ignoreManifest: true, version: '1.2.3', channel: 'stable', + ...override, }) mockConfig.configDir = path.join(process.cwd(), './test/data/configs') mockConfig.dataDir = path.join(process.cwd(), './test/data') - mockConfig.version = '1.2.3' + mockConfig.version = override?.version ?? '1.2.3' return mockConfig }