-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathgenerateDocs.ts
More file actions
167 lines (128 loc) · 6.14 KB
/
Copy pathgenerateDocs.ts
File metadata and controls
167 lines (128 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import * as fs from 'fs';
import * as core from '@actions/core';
import * as path from 'path';
import { getGitHubMetadata } from './utils';
const FEATURES_README_TEMPLATE = `
# #{Name}
#{Description}
## Example Usage
\`\`\`json
"features": {
"#{Registry}/#{Namespace}/#{Id}:#{Version}": {}
}
\`\`\`
#{OptionsTable}
#{Notes}
---
_Note: This file was auto-generated from the [devcontainer-feature.json](#{RepoUrl}). Add additional notes to a \`NOTES.md\`._
`;
const TEMPLATE_README_TEMPLATE = `
# #{Name}
#{Description}
#{OptionsTable}
#{Notes}
---
_Note: This file was auto-generated from the [devcontainer-template.json](#{RepoUrl}). Add additional notes to a \`NOTES.md\`._
`;
export async function generateFeaturesDocumentation(basePath: string, ociRegistry: string, namespace: string, defaultBranch: string) {
await _generateDocumentation(basePath, FEATURES_README_TEMPLATE, 'devcontainer-feature.json', defaultBranch, ociRegistry, namespace);
}
export async function generateTemplateDocumentation(basePath: string, defaultBranch: string) {
await _generateDocumentation(basePath, TEMPLATE_README_TEMPLATE, 'devcontainer-template.json', defaultBranch);
}
async function _generateDocumentation(basePath: string, readmeTemplate: string, metadataFile: string, defaultBranch: string, ociRegistry: string = '', namespace: string = '') {
const directories = fs.readdirSync(basePath);
await Promise.all(
directories.map(async (f: string) => {
if (!f.startsWith('.')) {
const readmePath = path.join(basePath, f, 'README.md');
// Reads in feature.json
const jsonPath = path.join(basePath, f, metadataFile);
if (!fs.existsSync(jsonPath)) {
core.info(`(!) Warning: ${metadataFile} not found at path '${jsonPath}'. Skipping...`);
return;
}
let parsedJson: any | undefined = undefined;
try {
parsedJson = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
} catch (err) {
core.error(`Failed to parse ${jsonPath}: ${err}`);
return;
}
if (!parsedJson || !parsedJson?.id) {
core.error(`${metadataFile} for '${f}' does not contain an 'id'`);
return;
}
const srcInfo = getGitHubMetadata();
// Add version
let version = 'latest';
const parsedVersion: string = parsedJson?.version;
if (parsedVersion) {
// example - 1.0.0
const splitVersion = parsedVersion.split('.');
version = splitVersion[0];
}
const generateOptionsMarkdown = () => {
const options = parsedJson?.options;
if (!options) {
return '';
}
const keys = Object.keys(options);
const contents = keys
.map(k => {
const val = options[k];
const desc = val.description || '-';
const type = val.type || '-';
const def = val.default !== '' ? val.default : '-';
return `| ${k} | ${desc} | ${type} | ${def} |`;
})
.join('\n');
return '## Options\n\n' + '| Options Id | Description | Type | Default Value |\n' + '|-----|-----|-----|-----|\n' + contents;
};
const generateNotesMarkdown = () => {
const notesPath = path.join(basePath, f, 'NOTES.md');
return fs.existsSync(notesPath) ? fs.readFileSync(path.join(notesPath), 'utf8') : '';
};
let urlToConfig = `${metadataFile}`;
const basePathTrimmed = basePath.startsWith('./') ? basePath.substring(2) : basePath;
if (srcInfo.owner && srcInfo.repo) {
urlToConfig = `https://github.com/${srcInfo.owner}/${srcInfo.repo}/blob/${defaultBranch}/${basePathTrimmed}/${f}/${metadataFile}`;
}
let header;
const isDeprecated = parsedJson?.deprecated;
const hasLegacyIds = parsedJson?.legacyIds && parsedJson?.legacyIds.length > 0;
if (isDeprecated || hasLegacyIds) {
header = '### **IMPORTANT NOTE**\n';
if (isDeprecated) {
header += `- **This Feature is deprecated, and will no longer receive any further updates/support.**\n`;
}
if (hasLegacyIds) {
const formattedLegacyIds = parsedJson.legacyIds.map((legacyId: string) => `'${legacyId}'`);
header += `- **Ids used to publish this Feature in the past - ${formattedLegacyIds.join(', ')}**\n`;
}
}
let newReadme = readmeTemplate
// Templates & Features
.replace('#{Id}', parsedJson.id)
.replace('#{Name}', parsedJson.name ? `${parsedJson.name} (${parsedJson.id})` : `${parsedJson.id}`)
.replace('#{Description}', parsedJson.description ?? '')
.replace('#{OptionsTable}', generateOptionsMarkdown())
.replace('#{Notes}', generateNotesMarkdown())
.replace('#{RepoUrl}', urlToConfig)
// Features Only
.replace('#{Registry}', ociRegistry)
.replace('#{Namespace}', namespace)
.replace('#{Version}', version);
if (header) {
newReadme = header + newReadme;
}
// Remove previous readme
if (fs.existsSync(readmePath)) {
fs.unlinkSync(readmePath);
}
// Write new readme
fs.writeFileSync(readmePath, newReadme);
}
})
);
}