Skip to content

Commit 1e186f7

Browse files
committed
fix(migrate): write empty sections properly
Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
1 parent 8ac0580 commit 1e186f7

File tree

13 files changed

+233
-76
lines changed

13 files changed

+233
-76
lines changed

.github/workflows/main-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
with:
5050
ci-dokumentor-version: ${{ needs.ci.outputs.image-tag }}
5151
source: action.yml
52-
include-sections: badges,contributing,license,generated
52+
exclude-sections: overview,usage,inputs,outputs,secrets,examples
5353
extra-badges: |
5454
[
5555
{
@@ -76,7 +76,7 @@ jobs:
7676
ci-dokumentor-version: ${{ needs.ci.outputs.image-tag }}
7777
source: action.yml
7878
destination: packages/docs/content/integrations/github-action.md
79-
include-sections: usage,inputs,outputs,secrets
79+
exclude-sections: header,badges,overview,contributing,security,license,generated
8080
github-token: ${{ secrets.GITHUB_TOKEN }}
8181
format-link: full
8282

packages/cicd/github-actions/src/migration/abstract-migration.adapter.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,16 @@ export abstract class AbstractMigrationAdapter implements MigrationAdapter {
295295
const findInsertAfterSection = (contentString: string, sectionName: SectionIdentifier): number => {
296296
const endMarker = formatterAdapter.sectionEnd(sectionName).toString('utf-8');
297297
const idx = contentString.lastIndexOf(endMarker);
298-
if (idx !== -1) return idx + endMarker.length;
298+
if (idx !== -1) {
299+
return idx + endMarker.length;
300+
}
299301

300302
// If there's a start marker but no end (malformed), insert after start
301303
const startMarker = formatterAdapter.sectionStart(sectionName).toString('utf-8');
302304
const sidx = result.lastIndexOf(startMarker);
303-
if (sidx !== -1) return sidx + startMarker.length;
305+
if (sidx !== -1) {
306+
return sidx + startMarker.length;
307+
}
304308

305309
return -1;
306310
};
@@ -313,7 +317,9 @@ export abstract class AbstractMigrationAdapter implements MigrationAdapter {
313317
let anchorIndex = -1;
314318
for (let i = 0; i < expectedSections.length; i++) {
315319
const sek = expectedSections[i];
316-
if (sek === missingSection) break;
320+
if (sek === missingSection) {
321+
break;
322+
}
317323
if (presentSections.has(sek)) {
318324
const candidate = findInsertAfterSection(contentString, sek);
319325
if (candidate !== -1 && candidate > anchorIndex) {
@@ -336,7 +342,7 @@ export abstract class AbstractMigrationAdapter implements MigrationAdapter {
336342
Buffer.from(sep),
337343
formatterAdapter.lineBreak(),
338344
sectionContent,
339-
after.trim().length > 0 ? Buffer.from(after) : formatterAdapter.lineBreak()
345+
after.trim().length > 0 ? Buffer.from(after) : Buffer.alloc(0)
340346
);
341347
} else {
342348
// No suitable anchor, append at end with a preceding newline

packages/cicd/github-actions/src/migration/action-docs-migration.adapter.e2e.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ Header here
104104
<!-- badges:start -->
105105
<!-- badges:end -->
106106
107+
107108
<!-- overview:start -->
108109
109110
Desc here

packages/cicd/github-actions/src/migration/github-action-readme-generator-migration.adapter.e2e.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Output details
155155
<!-- secrets:start -->
156156
<!-- secrets:end -->
157157
158+
158159
<!-- examples:start -->
159160
160161
ExampleX

packages/core/src/formatter/markdown/markdown-formatter.adapter.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,8 @@ Section Content
10561056

10571057
// Assert
10581058
expect(result.toString()).toEqual(`<!-- examples:start -->
1059-
<!-- examples:end -->`);
1059+
<!-- examples:end -->
1060+
`);
10601061
});
10611062
});
10621063

packages/core/src/formatter/markdown/markdown-formatter.adapter.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,12 @@ export class MarkdownFormatterAdapter implements FormatterAdapter {
235235
const endMarker = this.sectionEnd(section);
236236

237237
if (!input.length) {
238-
return this.appendContent(startMarker, this.lineBreak(), endMarker);
238+
return this.appendContent(
239+
startMarker,
240+
this.lineBreak(),
241+
endMarker,
242+
this.lineBreak()
243+
);
239244
}
240245

241246
return this.appendContent(

packages/core/src/renderer/file-renderer.adapter.e2e.spec.ts

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,28 @@ describe('FileRendererAdapter', () => {
2323
mockFs({
2424
'/test': {
2525
'document.md': 'Initial content\nSome text\n',
26-
'existing-section.md': [
27-
'Content before section',
28-
'<!-- examples:start -->',
29-
'Old section content',
30-
'<!-- examples:end -->',
31-
'Content after section',
32-
'',
33-
].join('\n'),
34-
'multiple-sections.md': [
35-
'Header content',
36-
'<!-- examples:start -->',
37-
'',
38-
'First section content',
39-
'',
40-
'<!-- examples:end -->',
41-
'Middle content',
42-
'<!-- usage:start -->',
43-
'Second section content',
44-
'<!-- usage:end -->',
45-
'Footer content',
46-
'',
47-
].join('\n'),
26+
'existing-section.md': `Content before section
27+
<!-- examples:start -->
28+
Old section content
29+
<!-- examples:end -->
30+
Content after section
31+
`,
32+
'multiple-sections.md': `Header content
33+
<!-- usage:start -->
34+
Second section content
35+
<!-- usage:end -->
36+
Middle content
37+
<!-- examples:start -->
38+
39+
First section content
40+
41+
<!-- examples:end -->
42+
<!-- security:start -->
43+
<!-- security:end -->
44+
<!-- license:start -->
45+
License content
46+
<!-- license:end -->
47+
`,
4848
'empty.md': '',
4949
},
5050
'/unsupported': {
@@ -77,11 +77,14 @@ describe('FileRendererAdapter', () => {
7777

7878
// Assert
7979
const fileContent = readFileSync(testFilePath, 'utf-8');
80-
expect(fileContent).toContain('Initial content');
81-
expect(fileContent).toContain('Some text');
82-
expect(fileContent).toContain(`<!-- ${testSectionIdentifier}:start -->`);
83-
expect(fileContent).toContain('New section content');
84-
expect(fileContent).toContain(`<!-- ${testSectionIdentifier}:end -->`);
80+
expect(fileContent).toEqual(`Initial content
81+
Some text
82+
<!-- examples:start -->
83+
84+
New section content
85+
86+
<!-- examples:end -->
87+
`);
8588
});
8689

8790
it('should replace existing section content when section already exists', async () => {
@@ -94,12 +97,14 @@ describe('FileRendererAdapter', () => {
9497

9598
// Assert
9699
const fileContent = readFileSync('/test/existing-section.md', 'utf-8');
97-
expect(fileContent).toContain('Content before section');
98-
expect(fileContent).toContain('Content after section');
99-
expect(fileContent).toContain(`<!-- ${testSectionIdentifier}:start -->`);
100-
expect(fileContent).toContain('New section content');
101-
expect(fileContent).toContain(`<!-- ${testSectionIdentifier}:end -->`);
102-
expect(fileContent).not.toContain('Old section content');
100+
expect(fileContent).toEqual(`Content before section
101+
<!-- examples:start -->
102+
103+
New section content
104+
105+
<!-- examples:end -->
106+
Content after section
107+
`);
103108
});
104109

105110
it('should handle empty files correctly', async () => {
@@ -124,29 +129,38 @@ New section content
124129
it('should handle multiple sections without affecting other sections', async () => {
125130
// Arrange
126131
const multipleSectionsAdapter = new FileRendererAdapter(new FileReaderAdapter());
127-
const newData = Buffer.from('Updated second section\n');
132+
const newUsageContent = Buffer.from('Updated usage section\n');
133+
const newLicenseContent = Buffer.from('Updated license section\n');
128134

129135
// Act
130136
await multipleSectionsAdapter.initialize('/test/multiple-sections.md', formatterAdapter);
131-
await multipleSectionsAdapter.writeSection(SectionIdentifier.Usage, newData);
137+
await multipleSectionsAdapter.writeSection(SectionIdentifier.Usage, newUsageContent);
138+
await multipleSectionsAdapter.writeSection(SectionIdentifier.Security, Buffer.alloc(0)); // Empty content for security section
139+
await multipleSectionsAdapter.writeSection(SectionIdentifier.License, newLicenseContent);
132140

133141
// Assert
134142
const fileContent = readFileSync('/test/multiple-sections.md', 'utf-8');
135143

136144
// Should preserve first section with blank lines around content
137145
expect(fileContent).toEqual(`Header content
146+
<!-- usage:start -->
147+
148+
Updated usage section
149+
150+
<!-- usage:end -->
151+
Middle content
138152
<!-- examples:start -->
139153
140154
First section content
141155
142156
<!-- examples:end -->
143-
Middle content
144-
<!-- usage:start -->
157+
<!-- security:start -->
158+
<!-- security:end -->
159+
<!-- license:start -->
145160
146-
Updated second section
161+
Updated license section
147162
148-
<!-- usage:end -->
149-
Footer content
163+
<!-- license:end -->
150164
`);
151165
});
152166

packages/core/src/renderer/file-renderer.adapter.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ export class FileRendererAdapter extends AbstractRendererAdapter {
2020
super();
2121
}
2222

23-
async replaceContent(data: ReadableContent): Promise<void> {
23+
async replaceContent(content: ReadableContent): Promise<void> {
2424
await this.safeWriteWithLock(async () => {
25-
return this.performReplaceContent(data);
25+
return this.performReplaceContent(content);
2626
});
2727
}
2828

29-
async writeSection(sectionIdentifier: SectionIdentifier, data: ReadableContent): Promise<void> {
29+
async writeSection(sectionIdentifier: SectionIdentifier, content: ReadableContent): Promise<void> {
3030
await this.safeWriteWithLock(async () => {
31-
return this.performWriteSection(sectionIdentifier, data);
31+
return this.performWriteSection(sectionIdentifier, content);
3232
});
3333
}
3434

@@ -44,11 +44,11 @@ export class FileRendererAdapter extends AbstractRendererAdapter {
4444
return undefined;
4545
}
4646

47-
private performReplaceContent(data: ReadableContent): Promise<void> {
47+
private performReplaceContent(content: ReadableContent): Promise<void> {
4848
const destination = this.getDestination();
4949

5050
return new Promise((resolve, reject) => {
51-
writeFile(destination, data, (err) => {
51+
writeFile(destination, content, (err) => {
5252
if (err) {
5353
reject(err);
5454
} else {
@@ -58,12 +58,20 @@ export class FileRendererAdapter extends AbstractRendererAdapter {
5858
});
5959
}
6060

61-
private performWriteSection(sectionIdentifier: SectionIdentifier, data: ReadableContent): Promise<void> {
61+
private performWriteSection(sectionIdentifier: SectionIdentifier, content: ReadableContent): Promise<void> {
6262
const destination = this.getDestination();
6363
const formatterAdapter = this.getFormatterAdapter();
6464

65-
// Look for the section in the file, replace content if it exists, or append if it doesn't.
65+
const sectionContent = formatterAdapter.section(
66+
sectionIdentifier,
67+
content
68+
);
69+
70+
const sectionStart = formatterAdapter.sectionStart(sectionIdentifier).toString();
71+
const sectionEnd = formatterAdapter.sectionEnd(sectionIdentifier).toString();
6672

73+
74+
// Look for the section in the file, replace content if it exists, or append if it doesn't.
6775
// Read file line by line to find the section
6876
return new Promise((resolve, reject) => {
6977
try {
@@ -87,15 +95,6 @@ export class FileRendererAdapter extends AbstractRendererAdapter {
8795
let inSection = false;
8896
let output: ReadableContent = Buffer.alloc(0);
8997

90-
91-
const sectionContent = formatterAdapter.section(
92-
sectionIdentifier,
93-
data
94-
);
95-
96-
const sectionStart = formatterAdapter.sectionStart(sectionIdentifier).toString();
97-
const sectionEnd = formatterAdapter.sectionEnd(sectionIdentifier).toString();
98-
9998
readLine.on('line', (line) => {
10099
const isSectionStart = line.trim() === sectionStart;
101100
if (isSectionStart) {

packages/core/src/renderer/renderer.adapter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ export interface RendererAdapter {
1919

2020
getDestination(): string;
2121

22-
writeSection(sectionIdentifier: string, data: ReadableContent): Promise<void>;
22+
writeSection(sectionIdentifier: string, content: ReadableContent): Promise<void>;
2323

2424
/**
2525
* Replace the entire content at the destination with the provided data.
2626
* This is useful for migration scenarios where the entire content needs
2727
* to be transformed and replaced, rather than appending sections.
2828
*/
29-
replaceContent(data: ReadableContent): Promise<void>;
29+
replaceContent(content: ReadableContent): Promise<void>;
3030

3131
/**
3232
* Finalize the rendering process and cleanup initialization.

packages/docs/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
# Site
1+
# Docs Package
2+
3+
The `@ci-dokumentor/docs` package contains the documentation site for the project.
24

35
This site is built using [Docusaurus](https://docusaurus.io/), a modern static site generator.

0 commit comments

Comments
 (0)