diff --git a/src/commands/config.ts b/src/commands/config.ts new file mode 100644 index 0000000..f1ed458 --- /dev/null +++ b/src/commands/config.ts @@ -0,0 +1,36 @@ +import {Flags} from '@oclif/core'; + +import HackMDCommand from '../command'; +import config from '../config'; +import {getConfigFilePath} from '../utils'; + +export default class Config extends HackMDCommand { + static description + = 'Show config file path and current configuration (credentials are never printed)'; + static examples = [ + `$ hackmd-cli config +Config file: /Users/you/.hackmd/config.json +hackmdAPIEndpointURL: https://api.hackmd.io/v1 +accessToken: (set)`, + `$ hackmd-cli config --path +/Users/you/.hackmd/config.json`, + ]; + static flags = { + help: Flags.help({char: 'h'}), + path: Flags.boolean({description: 'print only the config file path'}), + }; + + async run() { + const {flags} = await this.parse(Config); + const configFilePath = getConfigFilePath(); + + if (flags.path) { + this.log(configFilePath); + return; + } + + this.log(`Config file: ${configFilePath}`); + this.log(`hackmdAPIEndpointURL: ${config.hackmdAPIEndpointURL}`); + this.log(`accessToken: ${config.accessToken ? '(set)' : '(not set)'}`); + } +} diff --git a/test/config-command.test.ts b/test/config-command.test.ts new file mode 100644 index 0000000..12cf1b9 --- /dev/null +++ b/test/config-command.test.ts @@ -0,0 +1,76 @@ +import {expect} from 'chai'; +import path from 'node:path'; + +import {tempDir} from './utils'; + +const SECRET_TOKEN = 'super-secret-token-value'; + +function loadConfigCommand() { + // config is read at import time, so reload modules after setting env + delete require.cache[require.resolve('../src/config')]; + delete require.cache[require.resolve('../src/commands/config')]; + return require('../src/commands/config').default; +} + +async function runConfig( + flags: {path?: boolean}, + env: Record, +) { + const originalEnv = process.env; + process.env = {...env}; + + const lines: string[] = []; + try { + const ConfigCommand = loadConfigCommand(); + const command = { + log: (message = '') => lines.push(message), + parse: async () => ({flags}), + }; + await ConfigCommand.prototype.run.call(command); + } finally { + process.env = originalEnv; + } + + return lines.join('\n'); +} + +describe('Config command', () => { + it('prints the config file path', async () => { + const configDir = tempDir(); + const output = await runConfig({}, {HMD_CLI_CONFIG_DIR: configDir}); + + expect(output).to.include(path.join(configDir, 'config.json')); + }); + + it('never prints the access token value', async () => { + const output = await runConfig( + {}, + { + HMD_API_ACCESS_TOKEN: SECRET_TOKEN, + HMD_CLI_CONFIG_DIR: tempDir(), + }, + ); + + expect(output).to.not.include(SECRET_TOKEN); + expect(output).to.include('accessToken: (set)'); + }); + + it('shows (not set) when no access token is configured', async () => { + const output = await runConfig({}, {HMD_CLI_CONFIG_DIR: tempDir()}); + + expect(output).to.include('accessToken: (not set)'); + }); + + it('prints only the path with --path', async () => { + const configDir = tempDir(); + const output = await runConfig( + {path: true}, + { + HMD_API_ACCESS_TOKEN: SECRET_TOKEN, + HMD_CLI_CONFIG_DIR: configDir, + }, + ); + + expect(output).to.equal(path.join(configDir, 'config.json')); + }); +});