forked from product-os/flowzone
-
Notifications
You must be signed in to change notification settings - Fork 1
/
build.js
198 lines (170 loc) · 6.31 KB
/
build.js
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
const fs = require('fs');
const yaml = require('yaml');
// Using yaml.parse means that anything not supported by json will be dropped, in practice
// this means that yaml anchors will be "exploded" and not present in the github workflow
// we write out, which means we can use anchors when developing flowzone but not have to
// worry about github actions not supporting them
const flowzone = yaml.parse(fs.readFileSync('./flowzone.yml', 'utf8'), {
merge: true,
});
delete flowzone['.flowzone'];
fs.writeFileSync(
'./.github/workflows/flowzone.yml',
'# DO NOT EDIT MANUALLY - This file is auto-generated from `/flowzone.yml`\n' +
yaml.stringify(flowzone, {
// Disable aliasing of duplicate objects to ensure none of the anchors are
// auto-detected and added back into the output file
aliasDuplicateObjects: false,
// Disable line-wrapping, it looks very confusing to have embedded scripts be
// line-wrapped and will be a red herring if we ever need to debug
lineWidth: 0,
}),
);
function injectYamlIntoMarkdown(startTag, endTag) {
const yamlFilePath = '.github/workflows/flowzone.yml';
const markdownFilePath = 'README.md';
// Read the YAML file
const yamlContents = fs.readFileSync(yamlFilePath, 'utf8');
// Parse the YAML contents
const yamlData = yaml.parse(yamlContents);
// Generate the injected YAML code
let injectedYamlCode = `
name: Flowzone
on:
pull_request:
types: [opened, synchronize, closed]
branches: [main, master]
# allow external contributions to use secrets within trusted code
pull_request_target:
types: [opened, synchronize, closed]
branches: [main, master]
jobs:
flowzone:
name: Flowzone
uses: product-os/flowzone/.github/workflows/flowzone.yml@master
# prevent duplicate workflow executions for pull_request and pull_request_target
if: |
(
github.event.pull_request.head.repo.full_name == github.repository &&
github.event_name == 'pull_request'
) || (
github.event.pull_request.head.repo.full_name != github.repository &&
github.event_name == 'pull_request_target'
)
# Workflows in the same org or enterprise can use the inherit keyword to implicitly pass secrets
secrets: inherit
# Otherwise you must manually specify which secrets to pass
`;
// Function to handle multiline comments
function formatMultilineComment(value, wrapLength = 90) {
// Convert value to a string if it's not already
let valueStr = typeof value === 'string' ? value : JSON.stringify(value);
// Trim whitespace from the start and end of the value
valueStr = valueStr.trim();
// Check if the value is a multiline string
const isMultiline = valueStr.includes('\n');
// Split multiline strings and prepend each line with a comment sign and spaces
const valueLines = valueStr.split('\n');
let formattedComment = '';
valueLines.forEach((line, _) => {
let prefix = isMultiline ? `# ` : `# `;
const wrappedPrefix = `# `;
// Break long comments into chunks
const words = line.split(' ');
let commentLine = '';
for (const word of words) {
if (commentLine.length + word.length <= wrapLength) {
commentLine += word + ' ';
} else {
formattedComment += ` ${prefix}${commentLine.trim()}\n`;
commentLine = word + ' ';
// Reset the prefix to the wrapped version without label
prefix = wrappedPrefix;
}
}
// Add the remaining words
if (commentLine.trim().length > 0) {
formattedComment += ` ${prefix}${commentLine.trim()}\n`;
}
});
return formattedComment;
}
// Function to handle multiline strings
function formatMultilineString(value) {
// Convert value to a string if it's not already
let valueStr = typeof value === 'string' ? value : JSON.stringify(value);
// Trim whitespace from the start and end of the value
valueStr = valueStr.trim();
// Check if the value is a multiline string
const isMultiline = valueStr.includes('\n');
// If it's a multiline string, use the folded block syntax, otherwise return the string as is
return isMultiline
? `>\n ${valueStr.split('\n').join('\n ')}`
: valueStr;
}
// Convert secrets to structs in YAML
const secrets = yamlData.on.workflow_call.secrets;
if (secrets) {
injectedYamlCode += ` secrets:\n`;
Object.keys(secrets).forEach((key) => {
const { description, required } = secrets[key];
if (description != null) {
injectedYamlCode += formatMultilineComment(description);
}
if (required != null) {
injectedYamlCode += ` # Required: ${required}\n`;
}
injectedYamlCode += ` ${key}:\n\n`;
});
}
// Convert inputs to structs in YAML
const inputs = yamlData.on.workflow_call.inputs;
if (inputs) {
injectedYamlCode += ` with:\n`;
Object.keys(inputs).forEach((key) => {
const {
description,
type,
default: defaultValue,
required,
} = inputs[key];
if (description != null) {
injectedYamlCode += formatMultilineComment(description);
}
if (type != null) {
injectedYamlCode += ` # Type: ${type}\n`;
}
if (required != null) {
injectedYamlCode += ` # Required: ${required}\n`;
}
if (defaultValue != null) {
const formattedDefaultValue = formatMultilineString(defaultValue);
injectedYamlCode += ` ${key}: ${formattedDefaultValue}\n\n`;
} else {
injectedYamlCode += ` ${key}:\n\n`;
}
});
}
// Read the Markdown file
let markdownContents = fs.readFileSync(markdownFilePath, 'utf8');
// Find the start and end positions of the tags
const startIndex = markdownContents.indexOf(startTag);
const endIndex = markdownContents.indexOf(endTag);
// If the start and end tags are found
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
// Replace the content between the tags with the injected YAML code
markdownContents =
markdownContents.slice(0, startIndex + startTag.length) +
'\n\n<!---\n' +
'DO NOT EDIT MANUALLY - This section is auto-generated from flowzone.yml\n' +
'-->\n\n```yaml\n' +
injectedYamlCode +
'\n```\n' +
markdownContents.slice(endIndex);
// Write the modified content back to the Markdown file
fs.writeFileSync(markdownFilePath, markdownContents, 'utf8');
} else {
console.log('Start and/or end tags not found in the Markdown file.');
}
}
injectYamlIntoMarkdown('<!-- start usage -->', '<!-- end usage -->');