From a8519c0c3449233ca5f78a536d8e734bfde77ac0 Mon Sep 17 00:00:00 2001 From: gyroflaw <83718263+gyroflaw@users.noreply.github.com> Date: Thu, 20 Jul 2023 21:45:37 +0900 Subject: [PATCH 1/3] feat: fetch available versions from github tags --- package.json | 1 + src/commands/fetch-versions.ts | 43 ++++++++++++++++++++++++++++++++++ src/commands/node.ts | 6 +++++ src/config.ts | 1 + src/utils/check-version.ts | 31 ++++++++++++++++++++++++ yarn.lock | 28 ++++++++++++++++++++++ 6 files changed, 110 insertions(+) create mode 100644 src/commands/fetch-versions.ts create mode 100644 src/utils/check-version.ts diff --git a/package.json b/package.json index bd93d00..21789b1 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@oclif/core": "^2", "@oclif/plugin-help": "^5", "@oclif/plugin-plugins": "^2.4.7", + "axios": "^1.4.0", "chokidar": "^3.5.3", "envsub": "^4.1.0", "kleur": "^4.1.5", diff --git a/src/commands/fetch-versions.ts b/src/commands/fetch-versions.ts new file mode 100644 index 0000000..aaec8d6 --- /dev/null +++ b/src/commands/fetch-versions.ts @@ -0,0 +1,43 @@ +/* eslint-disable camelcase */ +import * as fs from "node:fs"; +import * as path from "node:path"; + +import axios from "axios"; +import { Command } from "@oclif/core"; + +import { WORK_DIR, githubTag } from "../config"; + +export interface Tag { + name: string; + zipball_url: string; + tarball_url: string; + commit: Commit; + node_id: string; +} + +export interface Commit { + sha: string; + url: string; +} + +export default class FetchVersion extends Command { + static description = "Fetch available casper node version from GitHub tags."; + + static args = {}; + + async run(): Promise { + const workDir = path.resolve(__dirname, "../..", WORK_DIR); + + const { data } = await axios.get(githubTag); + + const result = { + fetchedAt: Date.now(), + versions: data.map((tag) => tag.name), + }; + + fs.writeFileSync( + path.resolve(workDir, "versions.json"), + `${JSON.stringify(result)}` + ); + } +} diff --git a/src/commands/node.ts b/src/commands/node.ts index 639d5db..d2bf2d2 100644 --- a/src/commands/node.ts +++ b/src/commands/node.ts @@ -14,6 +14,7 @@ import { NODE_VERSIONS, WORK_DIR, } from "../config"; +import { checkVersion } from "../utils/check-version"; export default class Node extends Command { static description = "Starts a single Casper node."; @@ -38,11 +39,16 @@ export default class Node extends Command { static flags = { // can pass either --force or -f daemon: Flags.boolean({ char: "d" }), + "force-download": Flags.boolean({ char: "f" }), }; async run(): Promise { const { args, flags } = await this.parse(Node); + await checkVersion(args.branch, { + forceDownloadTags: flags["force-download"], + }); + ux.action.start("Donwloading assets"); await this.config.runCommand("download", [args.branch]); ux.action.stop(); diff --git a/src/config.ts b/src/config.ts index a8e803a..253ee82 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,6 +5,7 @@ export const CONFIG_DIR = `config/1_0_0`; export const nodeUrl = `https://s3.us-east-2.amazonaws.com/nctl.casperlabs.io/{GH_BRANCH}/casper-node`; export const chainSpecTemplate = `https://raw.githubusercontent.com/casper-network/casper-node/{GH_BRANCH}/resources/local/chainspec.toml.in`; export const configFile = `https://raw.githubusercontent.com/casper-network/casper-node/{GH_BRANCH}/resources/local/config.toml`; +export const githubTag = `https://api.github.com/repos/casper-network/casper-node/tags`; export const NODE_VERSIONS = [ "v0.9.0", diff --git a/src/utils/check-version.ts b/src/utils/check-version.ts new file mode 100644 index 0000000..fe01723 --- /dev/null +++ b/src/utils/check-version.ts @@ -0,0 +1,31 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; + +import FetchVersion from "../commands/fetch-versions"; + +import { WORK_DIR } from "../config"; + +interface Props { + forceDownloadTags?: boolean; +} + +export const checkVersion = async ( + version: string, + props?: Props +): Promise => { + const { forceDownloadTags = false } = props || {}; + const workDir = path.resolve(__dirname, "../..", WORK_DIR); + const versionFilePath = path.resolve(workDir, "versions.json"); + + // Save file tags if version file doesn't exist + if (!fs.existsSync(versionFilePath) || forceDownloadTags) { + await FetchVersion.run(); + } + + // load version file + const { versions }: { fetchedAt: number; versions: string[] } = JSON.parse( + fs.readFileSync(versionFilePath, { encoding: "utf-8" }) + ); + + return versions.includes(version); +}; diff --git a/yarn.lock b/yarn.lock index 066d7e4..9dbe4d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1308,6 +1308,15 @@ aws-sdk@^2.1231.0: uuid "8.0.0" xml2js "0.4.19" +axios@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -2623,6 +2632,11 @@ flatted@^3.1.0: resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz" integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -2647,6 +2661,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs-extra@^8.1: version "8.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" @@ -4859,6 +4882,11 @@ propagate@^2.0.0: resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pump@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" From f4be12aa6d90d0f3fa33f7f6891f07bfecbd28de Mon Sep 17 00:00:00 2001 From: gyroflaw <83718263+gyroflaw@users.noreply.github.com> Date: Mon, 24 Jul 2023 17:25:27 +0900 Subject: [PATCH 2/3] rename branch to version in command args and check versions --- src/commands/config.ts | 24 ++++++++++++++++-------- src/commands/download.ts | 27 +++++++++++++++++---------- src/commands/node.ts | 28 +++++++++++++++------------- src/commands/stop.ts | 21 ++------------------- src/config.ts | 35 ----------------------------------- src/utils/check-version.ts | 19 +++++++++++++++---- 6 files changed, 65 insertions(+), 89 deletions(-) diff --git a/src/commands/config.ts b/src/commands/config.ts index 27f0daa..bc76be3 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -7,24 +7,23 @@ import { Args, Command } from "@oclif/core"; // @ts-ignore import envsub from "envsub"; -import { CONFIG_DIR, NETWORK_NAMES, NODE_VERSIONS, WORK_DIR } from "../config"; +import { CONFIG_DIR, NETWORK_NAMES, WORK_DIR } from "../config"; +import { checkVersion, fetchLatestVersion } from "../utils/check-version"; export default class Config extends Command { static description = "Generate config files"; static args = { - branch: Args.string({ - name: "branch", // name of arg to show in help and reference with args[name] + version: Args.string({ + name: "version", // name of arg to show in help and reference with args[name] required: false, // make the arg required with `required: true` - description: "The branch to use", // help description - default: NODE_VERSIONS.at(-2)!, // use the latest release version - options: NODE_VERSIONS, // only allow input to be from a discrete set + description: "The version to use", // help description }), networkName: Args.string({ name: "networkName", // name of arg to show in help and reference with args[name] required: false, // make the arg required with `required: true` description: "The default network name", // help description - default: NETWORK_NAMES.at(-2)!, // use the latest release version + default: NETWORK_NAMES.at(-2)!, options: NETWORK_NAMES, // only allow input to be from a discrete set }), }; @@ -32,11 +31,20 @@ export default class Config extends Command { async run(): Promise { const { args } = await this.parse(Config); + // checks the version + const version = args.version || (await fetchLatestVersion()); + const isValidVersion = await checkVersion(version); + + if (!isValidVersion) { + this.logToStderr(`Not found version: ${version}`); + this.exit(-1); + } + const configDir = path.resolve( __dirname, "../..", WORK_DIR, - args.branch, + version, CONFIG_DIR ); if (!fs.existsSync(configDir)) { diff --git a/src/commands/download.ts b/src/commands/download.ts index 12ca098..4214d09 100644 --- a/src/commands/download.ts +++ b/src/commands/download.ts @@ -10,26 +10,33 @@ import { CONFIG_DIR, chainSpecTemplate, configFile, - NODE_VERSIONS, } from "../config"; +import { checkVersion, fetchLatestVersion } from "../utils/check-version"; export default class Download extends Command { static description = "Download required assets for running casper node."; static args = { - branch: Args.string({ - name: "branch", // name of arg to show in help and reference with args[name] + version: Args.string({ + name: "version", // name of arg to show in help and reference with args[name] required: false, // make the arg required with `required: true` - description: "The branch to use", // help description - default: NODE_VERSIONS.at(-2)!, // use the latest release version - options: NODE_VERSIONS, // only allow input to be from a discrete set + description: "The version to use", // help description }), }; async run(): Promise { const { args } = await this.parse(Download); - const workDir = path.resolve(__dirname, "../..", WORK_DIR, args.branch); + // checks the version + const version = args.version || (await fetchLatestVersion()); + const isValidVersion = await checkVersion(version); + + if (!isValidVersion) { + this.logToStderr(`Not found version: ${version}`); + this.exit(-1); + } + + const workDir = path.resolve(__dirname, "../..", WORK_DIR, version); const binDir = path.resolve(workDir, BIN_DIR); const configDir = path.resolve(workDir, CONFIG_DIR); @@ -43,7 +50,7 @@ export default class Download extends Command { if (!fs.existsSync(binaryPath)) { await download( - nodeUrl.replace("{GH_BRANCH}", args.branch), + nodeUrl.replace("{GH_BRANCH}", version), binaryPath, console.error ); @@ -57,7 +64,7 @@ export default class Download extends Command { if (!fs.existsSync(specPath)) { await download( - chainSpecTemplate.replace("{GH_BRANCH}", args.branch), + chainSpecTemplate.replace("{GH_BRANCH}", version), specPath, console.error ); @@ -65,7 +72,7 @@ export default class Download extends Command { if (!fs.existsSync(configPath)) { await download( - configFile.replace("{GH_BRANCH}", args.branch), + configFile.replace("{GH_BRANCH}", version), configPath, console.error ); diff --git a/src/commands/node.ts b/src/commands/node.ts index d2bf2d2..2d6591b 100644 --- a/src/commands/node.ts +++ b/src/commands/node.ts @@ -11,21 +11,18 @@ import { CONFIG_DIR, FUNDED_KEYS, NETWORK_NAMES, - NODE_VERSIONS, WORK_DIR, } from "../config"; -import { checkVersion } from "../utils/check-version"; +import { checkVersion, fetchLatestVersion } from "../utils/check-version"; export default class Node extends Command { static description = "Starts a single Casper node."; static args = { - branch: Args.string({ - name: "branch", // name of arg to show in help and reference with args[name] + version: Args.string({ + name: "version", // name of arg to show in help and reference with args[name] required: false, // make the arg required with `required: true` - description: "The branch to use", // help description - default: NODE_VERSIONS.at(-2)!, // use the latest release version - options: NODE_VERSIONS, // only allow input to be from a discrete set + description: "The version to use", // help description }), networkName: Args.string({ name: "networkName", // name of arg to show in help and reference with args[name] @@ -45,19 +42,24 @@ export default class Node extends Command { async run(): Promise { const { args, flags } = await this.parse(Node); - await checkVersion(args.branch, { - forceDownloadTags: flags["force-download"], - }); + // checks the version + const version = args.version || (await fetchLatestVersion()); + const isValidVersion = await checkVersion(version); + + if (!isValidVersion) { + this.logToStderr(`Not found version: ${version}`); + this.exit(-1); + } ux.action.start("Donwloading assets"); - await this.config.runCommand("download", [args.branch]); + await this.config.runCommand("download", [version]); ux.action.stop(); ux.action.start("Generating config files to run node"); - await this.config.runCommand("config", [args.branch, args.networkName]); + await this.config.runCommand("config", [version, args.networkName]); ux.action.stop(); - const workDir = path.resolve(__dirname, "../..", WORK_DIR, args.branch); + const workDir = path.resolve(__dirname, "../..", WORK_DIR, version); const binDir = path.resolve(workDir, BIN_DIR); const configDir = path.resolve(workDir, CONFIG_DIR); const binaryPath = path.resolve(binDir, "casper-node"); diff --git a/src/commands/stop.ts b/src/commands/stop.ts index 6c971a3..6ca6144 100644 --- a/src/commands/stop.ts +++ b/src/commands/stop.ts @@ -2,30 +2,13 @@ import * as fs from "node:fs"; import * as path from "node:path"; import shell from "shelljs"; -import { Args, Command } from "@oclif/core"; +import { Command } from "@oclif/core"; -import { NETWORK_NAMES, NODE_VERSIONS, WORK_DIR } from "../config"; +import { WORK_DIR } from "../config"; export default class Stop extends Command { static description = "Stop running node in background."; - static args = { - branch: Args.string({ - name: "branch", // name of arg to show in help and reference with args[name] - required: false, // make the arg required with `required: true` - description: "The branch to use", // help description - default: NODE_VERSIONS.at(-2)!, // use the latest release version - options: NODE_VERSIONS, // only allow input to be from a discrete set - }), - networkName: Args.string({ - name: "networkName", // name of arg to show in help and reference with args[name] - required: false, // make the arg required with `required: true` - description: "The default network name", // help description - default: NETWORK_NAMES.at(-2)!, // use the latest release version - options: NETWORK_NAMES, // only allow input to be from a discrete set - }), - }; - async run(): Promise { const { args } = await this.parse(Stop); diff --git a/src/config.ts b/src/config.ts index 253ee82..1147dd5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,41 +7,6 @@ export const chainSpecTemplate = `https://raw.githubusercontent.com/casper-netwo export const configFile = `https://raw.githubusercontent.com/casper-network/casper-node/{GH_BRANCH}/resources/local/config.toml`; export const githubTag = `https://api.github.com/repos/casper-network/casper-node/tags`; -export const NODE_VERSIONS = [ - "v0.9.0", - "v0.9.3", - "v0.9.3.1", - "v0.9.4", - "v1.0.0", - "v1.0.1", - "v1.1.0", - "v1.1.1", - "v1.1.2", - "v1.2.0", - "v1.2.1", - "v1.3.0", - "v1.3.1", - "v1.3.2", - "v1.3.3", - "v1.3.4", - "v1.4.0", - "v1.4.1", - "v1.4.2", - "v1.4.3", - "v1.4.4", - "v1.4.5", - "v1.4.6", - "v1.4.8", - "v1.4.9", - "v1.4.13", - "v1.4.14", - "v1.4.15", - "v1.4.15-alt", - "v1.5.0-rc.1", - "v1.5.1", - "dev", -]; - export const FUNDED_KEYS = [ { private: "MC4CAQAwBQYDK2VwBCIEII8ULlk1CJ12ZQ+bScjBt/IxMAZNggClWqK56D1/7CbI", diff --git a/src/utils/check-version.ts b/src/utils/check-version.ts index fe01723..141f194 100644 --- a/src/utils/check-version.ts +++ b/src/utils/check-version.ts @@ -9,10 +9,7 @@ interface Props { forceDownloadTags?: boolean; } -export const checkVersion = async ( - version: string, - props?: Props -): Promise => { +export const getVersions = async (props?: Props): Promise => { const { forceDownloadTags = false } = props || {}; const workDir = path.resolve(__dirname, "../..", WORK_DIR); const versionFilePath = path.resolve(workDir, "versions.json"); @@ -26,6 +23,20 @@ export const checkVersion = async ( const { versions }: { fetchedAt: number; versions: string[] } = JSON.parse( fs.readFileSync(versionFilePath, { encoding: "utf-8" }) ); + return versions; +}; + +export const checkVersion = async ( + version: string, + props?: Props +): Promise => { + if (version === "dev") return true; + const versions = await getVersions(props); return versions.includes(version); }; + +export const fetchLatestVersion = async (props?: Props): Promise => { + const versions = await getVersions(props); + return versions[0]; +}; From 62677d292c734de0aa04093c4dc84d3e616c3fdf Mon Sep 17 00:00:00 2001 From: gyroflaw <83718263+gyroflaw@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:22:38 +0900 Subject: [PATCH 3/3] refactor version check --- src/commands/fetch-versions.ts | 43 ------------------------------ src/commands/node.ts | 13 +++++++++ src/utils/check-version.ts | 48 +++++++++++++++++----------------- src/utils/download.ts | 2 +- 4 files changed, 38 insertions(+), 68 deletions(-) delete mode 100644 src/commands/fetch-versions.ts diff --git a/src/commands/fetch-versions.ts b/src/commands/fetch-versions.ts deleted file mode 100644 index aaec8d6..0000000 --- a/src/commands/fetch-versions.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable camelcase */ -import * as fs from "node:fs"; -import * as path from "node:path"; - -import axios from "axios"; -import { Command } from "@oclif/core"; - -import { WORK_DIR, githubTag } from "../config"; - -export interface Tag { - name: string; - zipball_url: string; - tarball_url: string; - commit: Commit; - node_id: string; -} - -export interface Commit { - sha: string; - url: string; -} - -export default class FetchVersion extends Command { - static description = "Fetch available casper node version from GitHub tags."; - - static args = {}; - - async run(): Promise { - const workDir = path.resolve(__dirname, "../..", WORK_DIR); - - const { data } = await axios.get(githubTag); - - const result = { - fetchedAt: Date.now(), - versions: data.map((tag) => tag.name), - }; - - fs.writeFileSync( - path.resolve(workDir, "versions.json"), - `${JSON.stringify(result)}` - ); - } -} diff --git a/src/commands/node.ts b/src/commands/node.ts index 2d6591b..5b49576 100644 --- a/src/commands/node.ts +++ b/src/commands/node.ts @@ -115,6 +115,7 @@ export default class Node extends Command { }); let rpcStarted = false; + let speculativeStarted = false; let restStarted = false; let eventStreamStarted = false; @@ -152,6 +153,18 @@ export default class Node extends Command { kleur.green(`Started event stream server at http://127.0.0.1:9999`) ); } + + if ( + data.includes("started speculative execution server") && + !speculativeStarted + ) { + speculativeStarted = true; + console.info( + kleur.green( + `Started speculative execution server at http://127.0.0.1:7778` + ) + ); + } }); // log pid for further stop diff --git a/src/utils/check-version.ts b/src/utils/check-version.ts index 141f194..508e75e 100644 --- a/src/utils/check-version.ts +++ b/src/utils/check-version.ts @@ -1,42 +1,42 @@ +/* eslint-disable camelcase */ import * as fs from "node:fs"; import * as path from "node:path"; +import axios from "axios"; -import FetchVersion from "../commands/fetch-versions"; +import { WORK_DIR, githubTag } from "../config"; -import { WORK_DIR } from "../config"; - -interface Props { - forceDownloadTags?: boolean; +export interface Tag { + name: string; + zipball_url: string; + tarball_url: string; + commit: Commit; + node_id: string; } -export const getVersions = async (props?: Props): Promise => { - const { forceDownloadTags = false } = props || {}; - const workDir = path.resolve(__dirname, "../..", WORK_DIR); - const versionFilePath = path.resolve(workDir, "versions.json"); +export interface Commit { + sha: string; + url: string; +} - // Save file tags if version file doesn't exist - if (!fs.existsSync(versionFilePath) || forceDownloadTags) { - await FetchVersion.run(); - } +export const getVersions = async (): Promise => { + const { data } = await axios.get(githubTag); + const versions = data.map((tag) => tag.name); - // load version file - const { versions }: { fetchedAt: number; versions: string[] } = JSON.parse( - fs.readFileSync(versionFilePath, { encoding: "utf-8" }) - ); return versions; }; -export const checkVersion = async ( - version: string, - props?: Props -): Promise => { +export const checkVersion = async (version: string): Promise => { if (version === "dev") return true; + const workDir = path.resolve(__dirname, "../..", WORK_DIR); + + // Return true for downloaded version + if (fs.existsSync(path.resolve(workDir, version))) return true; - const versions = await getVersions(props); + const versions = await getVersions(); return versions.includes(version); }; -export const fetchLatestVersion = async (props?: Props): Promise => { - const versions = await getVersions(props); +export const fetchLatestVersion = async (): Promise => { + const versions = await getVersions(); return versions[0]; }; diff --git a/src/utils/download.ts b/src/utils/download.ts index 497dc92..77e9561 100644 --- a/src/utils/download.ts +++ b/src/utils/download.ts @@ -14,7 +14,7 @@ export default async function download( const response = await fetch(url); if (!response.ok) { - throw new Error("Unable to access"); + throw new Error(`Unable to access to ${url}`); } return new Promise((resolve) => {