Skip to content

Commit 74c6a25

Browse files
Copilotneilime
authored andcommitted
feat: implement documentation aggregation system
Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
1 parent 412f233 commit 74c6a25

File tree

9 files changed

+663
-9
lines changed

9 files changed

+663
-9
lines changed

.env

Whitespace-only changes.

.github/scripts/generate-docs.js

100644100755
File mode changed.

.github/workflows/main-ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,28 @@ jobs:
3737
name: github-pages
3838
url: ${{ steps.deployment.outputs.url }}
3939
steps:
40+
- id: generate-github-actions-docs
41+
uses: hoverkraft-tech/ci-dokumentor@c46a1a108957237cf485103a80b060c35c7dba33 # 0.2.2
42+
with:
43+
source: .github/workflows/sync-docs-push.yml
44+
45+
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
46+
id: generate-token
47+
with:
48+
app-id: ${{ vars.CI_BOT_APP_ID }}
49+
private-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
50+
51+
- uses: hoverkraft-tech/ci-github-common/actions/create-and-merge-pull-request@b7dd413209df265bef8d7eb0efb117eaabc684c4 # 0.27.0
52+
with:
53+
github-token: ${{ steps.generate-token.outputs.token }}
54+
branch: docs/action-documentation-update
55+
title: "docs: update action documentation"
56+
body: Update action documentation
57+
commit-message: |
58+
docs: update action documentation
59+
60+
[skip ci]
61+
4062
- id: deployment
4163
uses: hoverkraft-tech/ci-github-publish/actions/deploy/github-pages@0.12.1
4264
with:
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<!-- header:start -->
2+
3+
# GitHub Reusable Workflow: Push Documentation Helper
4+
5+
<div align="center">
6+
<img src="https://opengraph.githubassets.com/ee8743035f4d97b88b8e03937037b13ca280a32c2398978b76e268b0fa4cf3b1/hoverkraft-tech/public-docs" width="60px" align="center" alt="Push Documentation Helper" />
7+
</div>
8+
9+
---
10+
11+
<!-- header:end -->
12+
<!-- badges:start -->
13+
14+
[![Release](https://img.shields.io/github/v/release/hoverkraft-tech/public-docs)](https://github.com/hoverkraft-tech/public-docs/releases)
15+
[![License](https://img.shields.io/github/license/hoverkraft-tech/public-docs)](http://choosealicense.com/licenses/mit/)
16+
[![Stars](https://img.shields.io/github/stars/hoverkraft-tech/public-docs?style=social)](https://img.shields.io/github/stars/hoverkraft-tech/public-docs?style=social)
17+
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/hoverkraft-tech/public-docs/blob/main/CONTRIBUTING.md)
18+
19+
<!-- badges:end -->
20+
<!-- overview:start -->
21+
22+
## Overview
23+
24+
Reusable workflow that bundles project docs and triggers public portal sync
25+
- Collects README and docs markdown, adds sync metadata, and uploads a short-lived artifact
26+
- Dispatches a repository event so hoverkraft-tech/public-docs can ingest and publish updates
27+
28+
<!-- overview:end -->
29+
<!-- usage:start -->
30+
31+
## Usage
32+
33+
```yaml
34+
name: Push Documentation Helper
35+
on:
36+
push:
37+
branches:
38+
- main
39+
jobs:
40+
sync-docs-dispatcher:
41+
uses: hoverkraft-tech/public-docs/.github/workflows/sync-docs-dispatcher.yml@4ab9c098ba58ed7f19ae6e879516c04ed5379d1a # copilot/centralize-project-documentation
42+
secrets:
43+
# GitHub token with write access to trigger repository_dispatch in public-docs
44+
# This input is required.
45+
PUBLIC_DOCS_TOKEN: ""
46+
with:
47+
# Path to documentation in source repo (default: docs)
48+
# Default: `docs`
49+
docs_path: docs
50+
51+
# Include README.md from repository root
52+
# Default: `true`
53+
include_readme: true
54+
```
55+
56+
<!-- usage:end -->
57+
<!-- inputs:start -->
58+
59+
## Inputs
60+
61+
### Workflow Call Inputs
62+
63+
| **Input** | **Description** | **Required** | **Type** | **Default** |
64+
| -------------------- | ---------------------------------------------------- | ------------ | ----------- | ----------- |
65+
| **`docs_path`** | Path to documentation in source repo (default: docs) | **false** | **string** | `docs` |
66+
| **`include_readme`** | Include README.md from repository root | **false** | **boolean** | `true` |
67+
68+
<!-- inputs:end -->
69+
<!-- secrets:start -->
70+
71+
## Secrets
72+
73+
| **Secret** | **Description** | **Required** |
74+
| ----------------------- | ---------------------------------------------------------------------------- | ------------ |
75+
| **`PUBLIC_DOCS_TOKEN`** | GitHub token with write access to trigger repository_dispatch in public-docs | **true** |
76+
77+
<!-- secrets:end -->
78+
<!-- outputs:start -->
79+
<!-- outputs:end -->
80+
<!-- examples:start -->
81+
<!-- examples:end -->
82+
<!-- contributing:start -->
83+
84+
## Contributing
85+
86+
Contributions are welcome! Please see the [contributing guidelines](https://github.com/hoverkraft-tech/public-docs/blob/main/CONTRIBUTING.md) for more details.
87+
88+
<!-- contributing:end -->
89+
<!-- security:start -->
90+
<!-- security:end -->
91+
<!-- license:start -->
92+
93+
## License
94+
95+
This project is licensed under the MIT License.
96+
97+
SPDX-License-Identifier: MIT
98+
99+
Copyright © 2025 hoverkraft-tech
100+
101+
For more details, see the [license](http://choosealicense.com/licenses/mit/).
102+
103+
<!-- license:end -->
104+
<!-- generated:start -->
105+
106+
---
107+
108+
This documentation was automatically generated by [CI Dokumentor](https://github.com/hoverkraft-tech/ci-dokumentor).
109+
110+
<!-- generated:end -->
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# Reusable workflow that bundles project docs and triggers public portal sync
2+
# - Collects README and docs markdown, adds sync metadata, and uploads a short-lived artifact
3+
# - Dispatches a repository event so hoverkraft-tech/public-docs can ingest and publish updates
4+
---
5+
name: Push Documentation Helper
6+
7+
on:
8+
workflow_call:
9+
inputs:
10+
docs_path:
11+
description: "Path to documentation in source repo (default: docs)"
12+
required: false
13+
type: string
14+
default: "docs"
15+
include_readme:
16+
description: "Include README.md from repository root"
17+
required: false
18+
type: boolean
19+
default: true
20+
secrets:
21+
PUBLIC_DOCS_TOKEN:
22+
description: "GitHub token with write access to trigger repository_dispatch in public-docs"
23+
required: true
24+
25+
jobs:
26+
prepare-and-dispatch:
27+
runs-on: ubuntu-latest
28+
permissions:
29+
contents: read
30+
31+
steps:
32+
- name: Checkout repository
33+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
34+
35+
- name: Prepare documentation bundle
36+
id: prepare
37+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
38+
env:
39+
SOURCE_REPO: ${{ github.repository }}
40+
INCLUDE_README: ${{ inputs.include_readme }}
41+
DOCS_PATH: ${{ inputs.docs_path }}
42+
with:
43+
script: |
44+
const fs = require('fs');
45+
const path = require('path');
46+
const { execSync } = require('child_process');
47+
48+
core.info(`📦 Preparing documentation bundle`);
49+
50+
// Get current branch name (default branch)
51+
const currentBranch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
52+
core.setOutput('branch', currentBranch);
53+
core.info(`Branch: ${currentBranch}`);
54+
55+
// Create artifact directory
56+
const artifactDir = 'documentation-artifact';
57+
fs.mkdirSync(artifactDir, { recursive: true });
58+
59+
// Function to add frontmatter
60+
function addFrontmatter(filePath, sourcePath) {
61+
const content = fs.readFileSync(filePath, 'utf8');
62+
const now = new Date().toISOString();
63+
64+
let newContent;
65+
66+
// Check if file already has frontmatter
67+
if (content.startsWith('---\n')) {
68+
// Extract existing frontmatter
69+
const endIndex = content.indexOf('\n---\n', 4);
70+
if (endIndex !== -1) {
71+
const existingFrontmatter = content.substring(4, endIndex);
72+
const bodyContent = content.substring(endIndex + 5);
73+
74+
// Add source metadata to existing frontmatter
75+
newContent = `---\n${existingFrontmatter}\nsource_repo: ${process.env.SOURCE_REPO}\nsource_path: ${sourcePath}\nsource_branch: ${currentBranch}\nlast_synced: ${now}\n---\n${bodyContent}`;
76+
} else {
77+
// Malformed frontmatter, treat as no frontmatter
78+
newContent = `---\nsource_repo: ${process.env.SOURCE_REPO}\nsource_path: ${sourcePath}\nsource_branch: ${currentBranch}\nlast_synced: ${now}\n---\n\n${content}`;
79+
}
80+
} else {
81+
// No frontmatter, add it
82+
newContent = `---\nsource_repo: ${process.env.SOURCE_REPO}\nsource_path: ${sourcePath}\nsource_branch: ${currentBranch}\nlast_synced: ${now}\n---\n\n${content}`;
83+
}
84+
85+
fs.writeFileSync(filePath, newContent);
86+
}
87+
88+
// Copy README if requested
89+
if (process.env.INCLUDE_README === 'true' && fs.existsSync('README.md')) {
90+
core.info('📄 Including README.md');
91+
fs.copyFileSync('README.md', path.join(artifactDir, 'README.md'));
92+
addFrontmatter(path.join(artifactDir, 'README.md'), 'README.md');
93+
}
94+
95+
// Copy documentation directory if it exists
96+
const docsPath = process.env.DOCS_PATH;
97+
if (fs.existsSync(docsPath)) {
98+
core.info(`📁 Copying documentation from ${docsPath}`);
99+
100+
// Find and copy all markdown files
101+
const findCmd = `find "${docsPath}" -type f \\( -name "*.md" -o -name "*.mdx" \\)`;
102+
const files = execSync(findCmd).toString().trim().split('\n').filter(f => f);
103+
104+
for (const file of files) {
105+
const relPath = file.replace(`${docsPath}/`, '');
106+
const targetFile = path.join(artifactDir, relPath);
107+
108+
// Create directory structure
109+
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
110+
111+
// Copy file
112+
fs.copyFileSync(file, targetFile);
113+
114+
// Add frontmatter
115+
const sourcePath = `${docsPath}/${relPath}`;
116+
addFrontmatter(targetFile, sourcePath);
117+
118+
core.info(` ✅ Copied: ${relPath}`);
119+
}
120+
} else {
121+
core.warning(`⚠️ Documentation path ${docsPath} not found`);
122+
}
123+
124+
// Create index file
125+
const now = new Date().toISOString();
126+
const repositoryName = process.env.SOURCE_REPO.split('/')[1]
127+
.replace(/[-_]+/g, ' ')
128+
.replace(/\b\w/g, ch => ch.toUpperCase());
129+
const indexContent = `---
130+
title: ${repositoryName}
131+
description: Documentation for ${repositoryName}
132+
---
133+
134+
# ${repositoryName}
135+
136+
Documentation for the ${repositoryName} project.
137+
138+
**Source Repository:** [${process.env.SOURCE_REPO}](https://github.com/${process.env.SOURCE_REPO})
139+
**Branch:** ${currentBranch}
140+
**Last Synced:** ${now}
141+
`;
142+
fs.writeFileSync(path.join(artifactDir, '_index.md'), indexContent);
143+
144+
// Show summary
145+
core.info('📊 Documentation bundle prepared:');
146+
const allFiles = execSync(`find "${artifactDir}" -type f`).toString().trim().split('\n');
147+
for (const file of allFiles) {
148+
core.info(` - ${file.replace(`${artifactDir}/`, '')}`);
149+
}
150+
151+
// Save artifact name for later use
152+
const slugifiedRepo = process.env.SOURCE_REPO.replace('/', '-').toLowerCase();
153+
const artifactName = `docs-${slugifiedRepo}-${github.run_id}`;
154+
return artifactName;
155+
156+
- name: Upload documentation artifact
157+
id: upload-artifact
158+
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
159+
with:
160+
name: ${{ steps.prepare.outputs.result }}
161+
path: documentation-artifact/
162+
retention-days: 1
163+
164+
- name: Dispatch to public-docs
165+
uses: peter-evans/repository-dispatch@5fc4efd1a4797ddb68ffd0714a238564e4cc0e6f # v4.0.0
166+
with:
167+
token: ${{ secrets.PUBLIC_DOCS_TOKEN }}
168+
repository: hoverkraft-tech/public-docs
169+
event-type: sync-documentation
170+
client-payload: |
171+
{
172+
"repository": "${{ github.repository }}",
173+
"run-id": "${{ github.run_id }}",
174+
"artifact-id": "${{ steps.upload-artifact.outputs.artifact-id }}",
175+
}
176+
177+
- name: Summary
178+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
179+
env:
180+
DOCS_PATH: ${{ inputs.docs_path }}
181+
INCLUDE_README: ${{ inputs.include_readme }}
182+
ARTIFACT_NAME: ${{ steps.prepare.outputs.result }}
183+
ARTIFACT_ID: ${{ steps.upload-artifact.outputs.artifact-id }}
184+
with:
185+
script: |
186+
await core.summary
187+
.addHeading('📤 Documentation Dispatch Summary', 2)
188+
.addRaw('\n')
189+
.addList([
190+
`**Docs Path**: ${process.env.DOCS_PATH}`,
191+
`**Include README**: ${process.env.INCLUDE_README}`,
192+
`**Artifact**: ${process.env.ARTIFACT_NAME} (ID: ${process.env.ARTIFACT_ID})`,
193+
])
194+
.addRaw('\n')
195+
.addRaw('The public-docs repository will download the artifact and publish the documentation to the portal.')
196+
.write();

0 commit comments

Comments
 (0)