Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01320f9748 | ||
|
|
91cdd434b6 | ||
|
|
ed8b053d7b | ||
|
|
8ae5b66824 | ||
|
|
563021aa0b | ||
|
|
13c35a5f65 | ||
|
|
ee1223c9f6 | ||
|
|
c368d0b546 | ||
|
|
ad06481408 | ||
|
|
a928669fc7 | ||
|
|
69d3313f90 | ||
|
|
04c1d68333 |
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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.',
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -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,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -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}`);
|
||||||
|
};
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user