Skip to content

Commit c897971

Browse files
committed
feat: support sfdx plugins with release notes or changelog
1 parent 2683286 commit c897971

File tree

4 files changed

+46
-12
lines changed

4 files changed

+46
-12
lines changed

messages/display.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ CLI version or tag for which to display release notes.
1414

1515
This hidden parameter is used in post install or update hooks.
1616

17+
# flags.plugin.summary
18+
19+
Plugin name for which to display release notes.
20+
1721
# examples
1822

1923
- Display release notes for the currently installed CLI version:

src/commands/info/releasenotes/display.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { marked } from 'marked';
1313
import * as TerminalRenderer from 'marked-terminal';
1414
import { Env } from '@salesforce/kit';
1515
import { Flags, SfCommand, loglevel } from '@salesforce/sf-plugins-core';
16-
import { Lifecycle, Logger, Messages } from '@salesforce/core';
16+
import { Lifecycle, Logger, Messages, SfError } from '@salesforce/core';
1717
import { AnyJson, JsonMap } from '@salesforce/ts-types';
1818
import { getInfoConfig } from '../../../shared/getInfoConfig';
1919
import { getReleaseNotes } from '../../../shared/getReleaseNotes';
@@ -40,6 +40,13 @@ export default class Display extends SfCommand<DisplayOutput> {
4040

4141
public static readonly examples = messages.getMessages('examples', [Display.helpers.join(', ')]);
4242

43+
public static args = [
44+
{
45+
name: 'plugin',
46+
description: messages.getMessage('flags.plugin.summary'),
47+
},
48+
];
49+
4350
public static readonly flags = {
4451
version: Flags.string({
4552
char: 'v',
@@ -54,7 +61,7 @@ export default class Display extends SfCommand<DisplayOutput> {
5461

5562
public async run(): Promise<DisplayOutput> {
5663
const logger = Logger.childFromRoot(this.constructor.name);
57-
const { flags } = await this.parse(Display);
64+
const { flags, args } = await this.parse(Display);
5865
const env = new Env();
5966

6067
const isHook = !!flags.hook;
@@ -70,9 +77,13 @@ export default class Display extends SfCommand<DisplayOutput> {
7077
}
7178

7279
try {
73-
const installedVersion = this.config.pjson.version;
80+
const [plugin] = args.plugin ? this.config.plugins.filter((p) => p.name === args.plugin) : [this.config];
81+
82+
if (!plugin) throw new SfError(`No plugin '${args.plugin as string}' found`);
83+
84+
const installedVersion = plugin.pjson.version;
7485

75-
const infoConfig = await getInfoConfig(this.config.root);
86+
const infoConfig = await getInfoConfig(plugin.root);
7687

7788
const { distTagUrl, releaseNotesPath, releaseNotesFilename } = infoConfig.releasenotes;
7889

@@ -90,7 +101,7 @@ export default class Display extends SfCommand<DisplayOutput> {
90101
renderer: new TerminalRenderer({ emoji: false }),
91102
});
92103

93-
tokens.unshift(marked.lexer(`# Release notes for '${this.config.bin}':`)[0]);
104+
tokens.unshift(marked.lexer(`# Release notes for '${plugin.name}':`)[0]);
94105

95106
if (flags.json) {
96107
const body = tokens.map((token) => token.raw).join(os.EOL);
@@ -104,7 +115,7 @@ export default class Display extends SfCommand<DisplayOutput> {
104115
if (env.getBoolean(HIDE_FOOTER)) {
105116
await Lifecycle.getInstance().emitTelemetry({ eventName: 'FOOTER_HIDDEN' });
106117
} else {
107-
const footer = messages.getMessage('footer', [this.config.bin, releaseNotesPath, HIDE_NOTES, HIDE_FOOTER]);
118+
const footer = messages.getMessage('footer', [plugin.name, releaseNotesPath, HIDE_NOTES, HIDE_FOOTER]);
108119
this.log(marked.parse(footer));
109120
}
110121
}

src/shared/parseReleaseNotes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ const parseReleaseNotes = (notes: string, version: string, baseUrl: string): mar
2222

2323
tokens = parsed.filter((token) => {
2424
// TODO: Could make header depth (2) a setting in oclif.info.releasenotes
25-
if (token.type === 'heading' && token.depth === 2) {
26-
const coercedVersion = semver.coerce(token.text).version;
25+
if (token.type === 'heading' && token.depth <= 2) {
26+
const coercedVersion = semver.coerce(token.text)?.version;
2727

2828
// We will use this to find the closest patch if passed version is not found
2929
versions.push(coercedVersion);

test/commands/info/releasenotes/display.test.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { shouldThrow } from '@salesforce/core/lib/testSetup';
1414
import { marked } from 'marked';
1515
import { Env } from '@salesforce/kit';
1616
import { Lifecycle } from '@salesforce/core';
17-
import { Config } from '@oclif/core';
17+
import { Config, Plugin } from '@oclif/core';
1818
import { SfCommand } from '@salesforce/sf-plugins-core';
1919
import * as getInfoConfig from '../../../../src/shared/getInfoConfig';
2020
import * as getReleaseNotes from '../../../../src/shared/getReleaseNotes';
@@ -39,6 +39,7 @@ describe('info:releasenotes:display', () => {
3939
let markedParserSpy: Sinon.SinonSpy;
4040

4141
const oclifConfigStub = fromStub(stubInterface<Config>(sandbox));
42+
const oclifPluginStub = fromStub(stubInterface<Plugin>(sandbox));
4243

4344
class TestDisplay extends Display {
4445
public async runIt() {
@@ -48,7 +49,8 @@ describe('info:releasenotes:display', () => {
4849
}
4950

5051
const runDisplayCmd = async (params: string[]) => {
51-
oclifConfigStub.bin = 'sfdx';
52+
oclifConfigStub.name = 'sfdx-cli';
53+
oclifPluginStub.name = 'sfdx-plugin';
5254

5355
const cmd = new TestDisplay(params, oclifConfigStub);
5456

@@ -70,6 +72,9 @@ describe('info:releasenotes:display', () => {
7072
oclifConfigStub.pjson.version = '3.3.3';
7173
oclifConfigStub.root = '/root/path';
7274

75+
oclifPluginStub.pjson.version = '3.3.3';
76+
oclifConfigStub.plugins = [oclifPluginStub];
77+
7378
getBooleanStub = stubMethod(sandbox, Env.prototype, 'getBoolean');
7479
getBooleanStub.withArgs('SFDX_HIDE_RELEASE_NOTES').returns(false);
7580
getBooleanStub.withArgs('SFDX_HIDE_RELEASE_NOTES_FOOTER').returns(false);
@@ -168,7 +173,21 @@ describe('info:releasenotes:display', () => {
168173
it('logs logs a header with cli bin', async () => {
169174
await runDisplayCmd([]);
170175

171-
expect(uxLogStub.args[0][0]).to.contain("# Release notes for 'sfdx':");
176+
expect(uxLogStub.args[0][0]).to.contain("# Release notes for 'sfdx-cli':");
177+
});
178+
179+
it('logs logs a header with plugin', async () => {
180+
await runDisplayCmd(['sfdx-plugin']);
181+
182+
expect(uxLogStub.args[0][0]).to.contain("# Release notes for 'sfdx-plugin':");
183+
});
184+
185+
it('throws an error if plugin name is invalid', async () => {
186+
try {
187+
await shouldThrow(runDisplayCmd(['no-plugin']));
188+
} catch (err) {
189+
expect((err as Error).message).to.contain("No plugin 'no-plugin' found");
190+
}
172191
});
173192

174193
it('calls getReleaseNotes with passed version', async () => {
@@ -257,7 +276,7 @@ describe('info:releasenotes:display', () => {
257276
const json = await runDisplayCmd(['--json']);
258277

259278
const expected = {
260-
body: `# Release notes for 'sfdx':${os.EOL}## Release notes for 3.3.3`,
279+
body: `# Release notes for 'sfdx-cli':${os.EOL}## Release notes for 3.3.3`,
261280
url: mockInfoConfig.releasenotes.releaseNotesPath,
262281
};
263282

0 commit comments

Comments
 (0)