Skip to content

Commit d978356

Browse files
committed
automate generation of cl node env files
1 parent 2b0c998 commit d978356

File tree

10 files changed

+18162
-3
lines changed

10 files changed

+18162
-3
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ _src/animate-chain-transitions.js
1111
_src/link-to-wallet.js
1212
_src/reference/chainsToBe.json
1313
.test/
14+
.env

_src/nodes-config.ts

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import nodesConfig from './reference/nodesConf.json';
2+
import { Octokit } from 'octokit';
3+
import { createTokenAuth } from '@octokit/auth-token';
4+
import fetch from 'node-fetch';
5+
import { writeFile, readFile } from 'fs/promises';
6+
import { normalize } from 'path';
7+
import { format } from 'prettier';
8+
9+
import * as dotenv from 'dotenv';
10+
dotenv.config();
11+
12+
const getTags = async (repo: string) => {
13+
const PAT = process.env.PAT;
14+
if (!PAT) throw new Error('PAT not found in .env');
15+
const auth = createTokenAuth(PAT);
16+
const authentication = await auth();
17+
const octokit = new Octokit({
18+
auth: authentication.token,
19+
});
20+
interface Node {
21+
tagName: string;
22+
}
23+
24+
interface Edge {
25+
node: Node;
26+
}
27+
28+
interface GetTagsResponse {
29+
repository: {
30+
releases: {
31+
edges: Edge[];
32+
};
33+
};
34+
}
35+
36+
const response = (await octokit.graphql(`{
37+
repository(name: "${nodesConfig.repo}", owner: "${nodesConfig.owner}") {
38+
releases(last: 100) {
39+
edges {
40+
node {
41+
tagName
42+
}
43+
}
44+
}
45+
}
46+
}`)) as GetTagsResponse;
47+
48+
return response.repository.releases.edges.map((edge) => edge.node.tagName);
49+
};
50+
51+
const filterTags = (tags: string[]) => {
52+
const currentTags = nodesConfig['current-tags'] as string[];
53+
// Test to follow this pattern: v0.8.1 , v0.8.11 , v0.11.12...Etc
54+
const pattern = new RegExp(/^v\d{1,2}\.\d{1,2}\.\d{1,2}$/);
55+
56+
return tags.filter((tag) => pattern.test(tag) && currentTags.indexOf(tag) === -1);
57+
};
58+
59+
const sortTags = (tags: string[]) => {
60+
return tags.sort((t1, t2) => {
61+
// remove v
62+
const tag1 = t1.slice(1),
63+
tag2 = t2.slice(1);
64+
const version1 = tag1.split('.').map((x) => parseInt(x));
65+
const version2 = tag2.split('.').map((x) => parseInt(x));
66+
if (version1.length !== version2.length) throw new Error(`cannot sort tags ${t1},${t2}`);
67+
for (let i = 0; i < version1.length; i++) {
68+
if (version1[i] < version2[i]) {
69+
return -1;
70+
} else if (version1[i] > version2[i]) {
71+
return 1;
72+
}
73+
}
74+
return 0;
75+
});
76+
};
77+
78+
const updateIndex = async (tags: { tagName: string; path: string }[]) => {
79+
if (tags.length === 0) return;
80+
const path = normalize(`./docs/chainlink-nodes/config/index.md`);
81+
let data = (await readFile(path)).toString();
82+
83+
for (const tag of tags) {
84+
const entry = `- [${tag.tagName}](${tag.path})`;
85+
data = data.replace('**Topics**', `**Topics**\n${entry}`);
86+
}
87+
88+
await writeFile(
89+
path,
90+
format(data, {
91+
parser: 'markdown',
92+
semi: true,
93+
trailingComma: 'es5',
94+
singleQuote: true,
95+
printWidth: 120,
96+
}),
97+
{
98+
flag: 'w',
99+
}
100+
);
101+
};
102+
103+
const updateTags = async (tags: { tagName: string; path: string }[]) => {
104+
if (tags.length === 0) return;
105+
for (const tag of tags) {
106+
(nodesConfig['current-tags'] as string[]).push(tag.tagName);
107+
}
108+
109+
const nodesConfigPath = normalize('./_src/reference/nodesConf.json');
110+
await writeFile(
111+
nodesConfigPath,
112+
format(JSON.stringify(nodesConfig), {
113+
parser: 'json',
114+
semi: true,
115+
trailingComma: 'es5',
116+
singleQuote: true,
117+
printWidth: 120,
118+
}),
119+
{
120+
flag: 'w',
121+
}
122+
);
123+
};
124+
125+
const writeConfigFile = async (tagName: string) => {
126+
const patternTagInFile = '__TAG__';
127+
const fileHeader = `
128+
---
129+
layout: nodes.liquid
130+
section: nodeOperator
131+
date: Last Modified
132+
title: "Configuring Chainlink Nodes"
133+
permalink: "docs/chainlink-nodes/config/${patternTagInFile}/"
134+
---
135+
`;
136+
137+
const tagInPath = tagName.replace(/\./g, '_');
138+
const clean = [
139+
{
140+
from: ':warning:',
141+
to: '⚠️',
142+
},
143+
];
144+
const path = normalize(`./docs/chainlink-nodes/config/${tagInPath}.md`);
145+
const url = normalize(
146+
`https://raw.githubusercontent.com/${nodesConfig.owner}/${nodesConfig.repo}/${tagName}/${nodesConfig.path}`
147+
);
148+
const response = await fetch(url);
149+
if (response.status === 200) {
150+
let data = await (await fetch(url)).text();
151+
for (const key in clean) {
152+
data = data.replace(new RegExp(clean[key].from, 'g'), clean[key].to);
153+
}
154+
const content = fileHeader.replace(patternTagInFile, tagInPath) + data;
155+
156+
await writeFile(
157+
path,
158+
format(content, {
159+
parser: 'markdown',
160+
semi: true,
161+
trailingComma: 'es5',
162+
singleQuote: true,
163+
printWidth: 120,
164+
}),
165+
{
166+
flag: 'w',
167+
}
168+
);
169+
170+
return { tagName, path: `/docs/chainlink-nodes/config/${tagInPath}/` };
171+
} else if (response.status !== 404) {
172+
throw new Error(`couldn't fetch ${url}. status ${response.status}`);
173+
}
174+
return { tagName: '', path: '' };
175+
};
176+
177+
const writeConfigFiles = async (tags: string[]) => {
178+
interface Result {
179+
success: { tagName: string; path: string }[];
180+
error: {
181+
tag: string;
182+
errorMessage: any;
183+
}[];
184+
}
185+
const result: Result = { success: [], error: [] };
186+
for (const tag of tags) {
187+
try {
188+
const successTag = await writeConfigFile(tag);
189+
if (successTag.tagName) result.success.push(successTag);
190+
} catch (error) {
191+
result.error.push({ tag, errorMessage: error });
192+
}
193+
}
194+
return result;
195+
};
196+
getTags(nodesConfig.repo).then(async (w) => {
197+
const sortedTags = sortTags(filterTags(w));
198+
const result = await writeConfigFiles(sortedTags);
199+
console.log(`config files generated ${JSON.stringify(result)}`);
200+
await updateIndex(result.success);
201+
await updateTags(result.success);
202+
if (result.error.length > 0) throw new Error(`an error happened ${JSON.stringify(result.error)}`);
203+
});

_src/reference/nodesConf.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"owner": "smartcontractkit",
3+
"repo": "chainlink",
4+
"path": "docs/CONFIG.md",
5+
"current-tags": ["v1.6.0", "v1.7.0", "v1.7.1", "v1.8.0"]
6+
}

docs/chainlink-nodes/config/index.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
layout: nodes.liquid
3+
section: nodeOperator
4+
date: Last Modified
5+
title: 'Configuring Chainlink Nodes'
6+
permalink: 'docs/chainlink-nodes/config/index/'
7+
---
8+
9+
**Topics**
10+
11+
- [v1.8.0](/docs/chainlink-nodes/config/v1_8_0/)
12+
- [v1.7.1](/docs/chainlink-nodes/config/v1_7_1/)
13+
- [v1.7.0](/docs/chainlink-nodes/config/v1_7_0/)
14+
- [v1.6.0](/docs/chainlink-nodes/config/v1_6_0/)

0 commit comments

Comments
 (0)