Skip to content

Commit be455d9

Browse files
committed
feat: Implemented changelog display in readme.txt
1 parent 938a22a commit be455d9

File tree

9 files changed

+218
-28
lines changed

9 files changed

+218
-28
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ The plugin can be configured in the [**semantic-release** configuration file](ht
3333
"slug" : "my-plugin",
3434
"withAssets": true,
3535
"withReadme": true,
36+
"withChangelog": true,
3637
"withVersionFile": true,
3738
}]
3839
]
@@ -43,6 +44,7 @@ With this example, for each release, the plugin will:
4344
* Verify that the plugin is valid
4445
* Create a zip file for the plugin (/tmp/wp-release/my-plugin.zip)
4546
* Create a zip file for the assets (/tmp/wp-release/assets.zip)
47+
* Write the release notes to the readme file (/tmp/wp-release/readme.txt)
4648
* Copy the readme.txt file (/tmp/wp-release/readme.txt)
4749
* Create a version file (/tmp/wp-release/version.txt)
4850

@@ -59,6 +61,7 @@ Plugin uses no environment variables, but has a lot of configuration options
5961
| `path` | The path of root folder that contains plugin or theme | `./` |
6062
| `withAssets` | Does the package have assets (screenshots, banners, logo) | `false` |
6163
| `withReadme` | Does the package have a readme.txt file | `false` |
64+
| `withChangelog` | Are we writing the release notes to the readme file | `false` |
6265
| `withVersionFile` | Do we need to create a file with the next release version | `true` |
6366
| `releasePath` | Base path for all of the release files | `/tmp/wp-release` |
6467
| `versionFiles` | Array of additional files containing the package version. | `[]` |

lib/classes/plugin-config.class.ts

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
IsNotEmpty,
55
IsOptional,
66
IsString,
7+
ValidateIf,
78
} from 'class-validator';
89

910
export class PluginConfig {
@@ -30,6 +31,14 @@ export class PluginConfig {
3031
@IsOptional()
3132
withReadme? = false;
3233

34+
/**
35+
* Are we generating a changelog?
36+
*/
37+
@IsBoolean()
38+
@IsOptional()
39+
@ValidateIf((o: PluginConfig) => o?.withReadme === true)
40+
withChangelog? = false;
41+
3342
/**
3443
* Do we output the version in a file?
3544
*/

lib/prepare.ts

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { PrepareContext } from 'semantic-release';
99
import { replaceVersions } from './utils/replace-versions.js';
1010
import { copyFiles } from './utils/copy-files.js';
1111
import { copyAssets } from './utils/copy-assets.js';
12+
import { prepareChangelog } from './utils/prepare-changelog.js';
1213

1314
function getReadmePath(workDir: string): string | undefined {
1415
return ['.wordpress-org/readme.txt', 'readme.txt'].reduce(
@@ -41,6 +42,15 @@ export async function prepare(
4142

4243
const readmePath = config.withReadme ? getReadmePath(workDir) : undefined;
4344

45+
if (readmePath && config.withChangelog) {
46+
errors.push(
47+
...(await prepareChangelog(
48+
path.resolve(workDir, readmePath),
49+
context.nextRelease.notes,
50+
)),
51+
);
52+
}
53+
4454
readmePath && files.push(readmePath);
4555

4656
errors.push(

lib/utils/prepare-changelog.ts

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import fs from 'fs-extra';
2+
import { marked } from 'marked';
3+
import SemanticReleaseError from '@semantic-release/error';
4+
import getError from './get-error.js';
5+
6+
function stripLink(text: string): string {
7+
return text.replace(/\[([^\]]+)]\([^)]+\)/g, '$1');
8+
}
9+
function stripEmoji(text: string): string {
10+
return text.replace(/:\w+:/g, '').trim();
11+
}
12+
13+
function stripCommitHash(text: string): string {
14+
return text.replace(/\(.+\)$/, '').trim();
15+
}
16+
17+
export async function prepareChangelog(
18+
readmePath: string,
19+
notes: string,
20+
): Promise<any> {
21+
const errors: SemanticReleaseError[] = [];
22+
const renderer = new marked.Renderer();
23+
24+
renderer.link = ({ text }) => text;
25+
renderer.heading = ({ text, depth }) => {
26+
if (depth === 2) {
27+
return `= ${stripLink(text)} =\n\n`;
28+
}
29+
30+
if (depth === 3) {
31+
return `**${stripEmoji(text)}**\n\n`;
32+
}
33+
34+
return `\n${text}\n\n`;
35+
};
36+
renderer.list = ({ items }) =>
37+
`${items.map((item) => renderer.listitem(item)).join('')}\n`;
38+
renderer.listitem = ({ text }) => `* ${stripCommitHash(stripLink(text))}\n`;
39+
40+
const output = (await marked(notes, { renderer }))
41+
.replace(/\n{3,}/g, '\n\n')
42+
.trim();
43+
44+
const readmeText = fs
45+
.readFileSync(readmePath, 'utf-8')
46+
.replace('== Changelog ==', `== Changelog ==\n\n${output}`);
47+
48+
try {
49+
fs.writeFileSync(readmePath, readmeText);
50+
} catch (err) {
51+
errors.push(getError('EFILECOPY', readmePath));
52+
}
53+
54+
return errors;
55+
}

0 commit comments

Comments
 (0)