Compare commits

...
Author SHA1 Message Date
Botond 01320f9748 fix: format 2022-08-26 19:39:10 +02:00
Botond 91cdd434b6 fix: imports 2022-08-26 19:38:15 +02:00
Botond ed8b053d7b fix: format 2022-08-26 18:55:11 +02:00
Botond 8ae5b66824 feat: add network config override 2022-08-26 18:54:33 +02:00
Botond 563021aa0b feat: add network update 2022-08-26 15:52:17 +02:00
Botond 13c35a5f65 chore: merge source 2022-08-26 13:38:26 +02:00
Botond ee1223c9f6 fix: add missing utils 2022-08-26 13:37:50 +02:00
Botond c368d0b546 chore: merge source 2022-08-26 13:33:52 +02:00
Botond ad06481408 fix: format 2022-08-26 13:33:19 +02:00
Botond a928669fc7 feat: merge source 2022-08-26 13:32:50 +02:00
Botond 69d3313f90 fix: format 2022-08-25 18:08:52 +02:00
Botond 04c1d68333 feat: add type update script 2022-08-25 17:47:40 +02:00
9 changed files with 487 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
const path = require('node:path');
const execWrap = require('./utils/exec-wrap');
const githubRequest = require('./utils/github-request');
const wrapCli = require('./utils/wrap-cli');
const launchGitWorkflow = require('./utils/git-workflow');
const launchGithubWorkflow = require('./utils/github-workflow');
const typesProjectJson = require(path.join(
__dirname,
'..',
'libs',
'types',
'project.json'
));
const TYPE_UPDATE_BRANCH = 'fix/types';
const cliArgsSpecs = [
{
name: 'apiUrl',
arg: 'url',
required: true,
validate: (value) => {
try {
new URL(value);
} catch (err) {
throw new Error(
`Invalid url found: ${value}. Make sure you pass in a valid url using the "--url" flag.`
);
}
},
},
{
name: 'apiVersion',
arg: 'version',
required: true,
},
{
name: 'apiCommitHash',
arg: 'commit',
required: true,
},
{
name: 'apiRepoName',
arg: 'repo',
default: 'vega',
},
{
name: 'apiRepoOwner',
arg: 'owner',
default: 'vegaprotocol',
},
{
name: 'frontendRepoName',
arg: 'fe-repo',
default: 'frontend-monorepo',
},
{
name: 'frontendRepoOwner',
arg: 'fe-owner',
default: 'vegaprotocol',
},
{
name: 'githubAuthToken',
arg: 'token',
required: true,
},
];
const getGenerateCmd = (projectJson) => {
if (
projectJson &&
projectJson.targets &&
projectJson.targets.generate &&
projectJson.targets.generate.options &&
projectJson.targets.generate.options.commands
) {
return projectJson.targets.generate.options.commands.join(' && ');
}
};
const run = async ({
apiUrl,
apiVersion,
apiRepoOwner,
apiRepoName,
apiCommitHash,
githubAuthToken,
frontendRepoOwner,
frontendRepoName,
}) => {
const generateCmd = getGenerateCmd(typesProjectJson);
execWrap({
cmd: `NX_VEGA_URL=${apiUrl} ${generateCmd}`,
errMessage:
'There was an error trying to regenerating the types for the frontend.',
});
const unstagedFiles = execWrap({
cmd: `git diff --name-only`,
errMessage: `Error listing unstaged files`,
})
.split('\n')
.filter((file) => file !== '');
if (unstagedFiles.length) {
launchGitWorkflow({
branchName: TYPE_UPDATE_BRANCH,
frontendRepoOwner,
frontendRepoName,
commitMessage: `update types for v${apiVersion} on HEAD:${apiCommitHash}`,
});
await launchGithubWorkflow({
frontendRepoOwner,
frontendRepoName,
githubAuthToken,
issueBody: {
title: `[automated] Update types for datanode v${apiVersion}`,
body: `Update the frontend based on the [datanode changes](https://github.com/${apiRepoOwner}/${apiRepoName}/commit/${apiCommitHash}).`,
},
prBody: {
head: TYPE_UPDATE_BRANCH,
title: 'Update types',
body: `Patches the frontend based on the [datanode changes](https://github.com/${apiRepoOwner}/${apiRepoName}/commit/${apiCommitHash}).`,
},
});
}
};
wrapCli(run, cliArgsSpecs);
+149
View File
@@ -0,0 +1,149 @@
const fs = require('node:fs');
const path = require('node:path');
const execWrap = require('./utils/exec-wrap');
const githubRequest = require('./utils/github-request');
const wrapCli = require('./utils/wrap-cli');
const launchGitWorkflow = require('./utils/git-workflow');
const launchGithubWorkflow = require('./utils/github-workflow');
const NETWORK_UPDATE_BRANCH = 'fix/networks';
const STATIC_APP_PATH = path.join(__dirname, '..', 'apps', 'static');
const NETWORK_CONFIG_PATH = path.join(STATIC_APP_PATH, 'src', 'assets');
const getJson = (value, errMessage) => {
try {
return JSON.parse(value);
} catch (err) {
throw new Error(errMessage);
}
};
const cliArgsSpecs = [
{
name: 'payload',
arg: 'payload',
required: true,
validate: (rawPayload) => {
const payload = getJson(
rawPayload,
'The payload must be a valid json object'
);
Object.keys(payload).forEach((network) => {
const item = payload[network] || {};
console.log(item);
if (typeof item.chainId !== 'string') {
throw new Error(
`The network "${network}" must have a valid chainId.`
);
}
if (!Array.isArray(item.hosts)) {
throw new Error(
`The network "${network}" must have a valid list of hosts.`
);
}
item.hosts.forEach((host) => {
try {
new URL(host);
} catch (err) {
throw new Error(
`The host "${host}" on the network "${network}" must be a valid url.`
);
}
});
});
},
},
{
name: 'networkCommitHash',
arg: 'commit',
required: true,
},
{
name: 'networkRepoName',
arg: 'repo',
required: true,
},
{
name: 'networkRepoOwner',
arg: 'owner',
default: 'vegaprotocol',
},
{
name: 'frontendRepoName',
arg: 'fe-repo',
default: 'frontend-monorepo',
},
{
name: 'frontendRepoOwner',
arg: 'fe-owner',
default: 'vegaprotocol',
},
{
name: 'githubAuthToken',
arg: 'token',
required: true,
},
];
const getNetworkConfigFileName = (network) =>
`${network.toLowerCase()}-network.json`;
const findTargetConfig = (network) => {
const fileName = getNetworkConfigFileName(network);
return path.join(NETWORK_CONFIG_PATH, fileName);
};
const run = async ({
payload,
networkCommitHash,
networkRepoName,
networkRepoOwner,
frontendRepoOwner,
frontendRepoName,
githubAuthToken,
}) => {
const networks = JSON.parse(payload);
for (let network in networks) {
const file = findTargetConfig(network);
if (fs.existsSync(file)) {
const fd = fs.openSync(file, 'w+');
fs.writeSync(fd, file, JSON.stringify(networks[network]));
}
}
const unstagedFiles = execWrap({
cmd: `git diff --name-only`,
errMessage: `Error listing unstaged files`,
})
.split('\n')
.filter((file) => file !== '');
console.log(frontendRepoOwner, frontendRepoName);
if (unstagedFiles.length) {
launchGitWorkflow({
branchName: NETWORK_UPDATE_BRANCH,
frontendRepoOwner,
frontendRepoName,
commitMessage: `update networks based on ${networkRepoOwner}/${networkRepoName} HEAD:${networkCommitHash}`,
});
await launchGithubWorkflow({
frontendRepoOwner,
frontendRepoName,
githubAuthToken,
issueBody: {
title: `[automated] Update network configuration`,
body: `Update the frontend based on the [datanode changes](https://github.com/${networkRepoOwner}/${networkRepoName}/commit/${networkCommitHash}).`,
},
prBody: {
head: NETWORK_UPDATE_BRANCH,
title: 'Update networks',
body: `Patches the frontend based on the [network changes](https://github.com/${networkRepoOwner}/${networkRepoName}/commit/${networkCommitHash}).`,
},
});
}
};
wrapCli(run, cliArgsSpecs);
+14
View File
@@ -0,0 +1,14 @@
const path = require('node:path');
const { execSync } = require('node:child_process');
const appRoot = path.join(__dirname, '..', '..');
module.exports = ({ cmd, errMessage }) => {
console.log(`executing: "${cmd}"`);
try {
const result = execSync(cmd, { cwd: appRoot, stdout: process.stdout });
return result.toString();
} catch (err) {
throw new Error(errMessage);
}
};
+54
View File
@@ -0,0 +1,54 @@
const execWrap = require('./exec-wrap');
module.exports = ({
branchName,
commitMessage,
frontendRepoOwner,
frontendRepoName,
}) => {
const remoteBranches = execWrap({
cmd: `git ls-remote --heads ssh://github.com/${frontendRepoOwner}/${frontendRepoName}.git ${branchName}`,
errMessage: `Error checking if the branch "${branchName}" exists on the origin.`,
});
const localBranches = execWrap({
cmd: 'git branch',
errMessage: `Error getting local branch names.`,
});
if (
remoteBranches.includes(branchName) ||
localBranches.includes(branchName)
) {
const currentBranchName = execWrap({
cmd: `git branch --show-current`,
errMessage: `Error getting current branch name.`,
});
if (currentBranchName !== branchName) {
execWrap({
cmd: `git checkout ${branchName}`,
errMessage: `There was an error trying to check out the branch "${branchName}".`,
});
}
} else {
execWrap({
cmd: `git checkout -b ${branchName}`,
errMessage: `There was an error trying to check out the branch "${branchName}".`,
});
}
execWrap({
cmd: `git add .`,
errMessage: `Error staging changed files.`,
});
execWrap({
cmd: `git commit -m 'chore: ${commitMessage}' --no-verify`,
errMessage: `Error checking if the branch "${branchName}" exists on the origin.`,
});
execWrap({
cmd: `git push -u ssh://github.com/${frontendRepoOwner}/${frontendRepoName}.git ${branchName} --no-verify`,
errMessage: 'Error pushing changes.',
});
};
+19
View File
@@ -0,0 +1,19 @@
const https = require('node:https');
const request = require('./request');
module.exports = async (url, { githubAuthToken, body }) => {
const options = {
method: 'POST',
headers: {
Accept: 'application/vnd.github+json',
Authorization: `token ${githubAuthToken}`,
'User-Agent': '',
},
};
options.agent = new https.Agent(options);
return request(url, {
...options,
body,
});
};
+46
View File
@@ -0,0 +1,46 @@
const githubRequest = require('./github-request');
module.exports = async ({
issueBody,
prBody,
frontendRepoOwner,
frontendRepoName,
githubAuthToken,
}) => {
const { number, html_url: issueHtmlUrl } = await githubRequest(
`https://api.github.com/repos/${frontendRepoOwner}/${frontendRepoName}/issues`,
{
githubAuthToken,
body: JSON.stringify(issueBody),
}
);
console.log(`Issue created: ${issueHtmlUrl}`);
const { html_url: prHtmlUrl } = await githubRequest(
`https://api.github.com/repos/${frontendRepoOwner}/${frontendRepoName}/pulls`,
{
githubAuthToken,
body: JSON.stringify({
base: prBody.base || 'master',
title: `fix/${number}: ${prBody.title}`,
head: prBody.head,
body: `
# Related issues 🔗
Closes #${number}
# Description
${prBody.body}
# Technical 👨‍🔧
This pull request was automatically generated.
`,
}),
}
);
console.log(`Pull request created: ${prHtmlUrl}`);
};
+22
View File
@@ -0,0 +1,22 @@
module.exports = ({ specs, args = [] }) => {
const defaultConfig = {
apiUrl: undefined,
apiVersion: undefined,
};
return specs.reduce((acc, spec) => {
const match = args.find((arg) => arg.startsWith(`--${spec.arg}=`));
const value = (match || '').replace(`--${spec.arg}=`, '');
if (spec.required && !value) {
throw new Error(`Cannot find required CLI argument "--${spec.arg}".`);
}
if (typeof spec.validate === 'function') {
spec.validate(value);
}
return {
...acc,
[spec.name]: value || spec.default,
};
}, {});
};
+37
View File
@@ -0,0 +1,37 @@
const https = require('node:https');
module.exports = (url, options) =>
new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => {
rawData += chunk.toString();
});
res.on('error', (err) => {
reject(err);
});
res.on('end', () => {
if (res.statusCode >= 400) {
reject(new Error(`HTTPS ${res.statusCode}: ${rawData}`));
return;
}
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (err) {
reject(err);
}
});
});
if (options.method === 'POST' && options.body) {
req.write(options.body);
}
req.on('error', (err) => {
reject(err);
});
req.end();
});
+13
View File
@@ -0,0 +1,13 @@
const parseCliArgs = require('./parse-cli-args');
module.exports = async function main(run, specs) {
try {
const config = parseCliArgs({
specs,
args: process.argv,
});
await run(config);
} catch (err) {
console.error(err);
}
};