diff --git a/.changeset/perfect-spoons-serve.md b/.changeset/perfect-spoons-serve.md new file mode 100644 index 0000000000..4b1dc9d81c --- /dev/null +++ b/.changeset/perfect-spoons-serve.md @@ -0,0 +1,5 @@ +--- +"@farcaster/hubble": patch +--- + +chore: Add cli options documentation linter diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b903e9965c..4448d8c9c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,6 +76,9 @@ jobs: # - name: Run audit # run: yarn audit + - name: Run build + run: yarn build + - name: Run linter run: yarn lint:ci diff --git a/apps/hubble/scripts/clidocs.cjs b/apps/hubble/scripts/clidocs.cjs new file mode 100644 index 0000000000..e184ba3364 --- /dev/null +++ b/apps/hubble/scripts/clidocs.cjs @@ -0,0 +1,42 @@ +const { execSync } = require("child_process"); +const fs = require("fs"); +const path = require("path"); + +module.exports = function clidocs() { + const docFileName = "www/docs/docs/cli.md"; + + try { + const helpOutput = execSync("node --no-warnings build/cli.js start --help").toString(); + + const optionNames = []; + const regex = /--\w+(-\w+)*/g; + let match; + + // rome-ignore lint/suspicious/noAssignInExpressions: + while ((match = regex.exec(helpOutput)) !== null) { + optionNames.push(match[0]); + } + + // Step 2: Read the contents of the cli.md file + const cliDocPath = path.resolve(__dirname, "..", docFileName); + const cliDocContent = fs.readFileSync(cliDocPath, "utf-8"); + + // Step 3: Check that each option name appears in the cli.md file + let anyError = false; + optionNames.forEach((optionName) => { + if (!cliDocContent.includes(optionName)) { + console.warn(`Documentation for option "${optionName}" is missing in ${docFileName}`); + anyError = true; + } + }); + + if (anyError) { + process.exit(1); + } else { + console.log("✨ CLI docs are up to date"); + } + } catch (error) { + console.error("Error executing command:", error); + process.exit(1); + } +}; diff --git a/apps/hubble/scripts/grafanadash.cjs b/apps/hubble/scripts/grafanadash.cjs new file mode 100644 index 0000000000..c5c0cb8542 --- /dev/null +++ b/apps/hubble/scripts/grafanadash.cjs @@ -0,0 +1,55 @@ +/** + * This is a custom linter that checks for the presence of a "datasource" property + * in the Grafana dashboard JSON file. If the property is present, it must be set + * to "Graphite". This is to ensure that the dashboard is not using any other + * datasource. + * + * It runs as a part of "yarn lint" + */ + +const fs = require("fs"); +const path = require("path"); + +module.exports = function grafana() { + const filePath = path.join(__dirname, "../grafana/grafana-dashboard.json"); + + function checkDataSource(lineArray) { + let lineNumber = 0; + + for (const line of lineArray) { + lineNumber++; + + // Check if the current line contains the undesired datasource entry + if (line.includes('"datasource":') && !line.replaceAll(" ", "").includes('"datasource":"Graphite"')) { + return lineNumber; + } + } + + return null; + } + + return new Promise((resolve, reject) => { + fs.readFile(filePath, "utf-8", (err, data) => { + if (err) { + console.error(`Error reading ${filePath}:`, err); + process.exit(1); + } + + // Split the content into lines + const lineArray = data.split("\n"); + const errorLine = checkDataSource(lineArray); + + if (errorLine !== null) { + console.error(`line ${errorLine}: ${lineArray[errorLine - 1]}`); + console.error(`Error: "datasource" has to be "Graphite"`); + console.error(`Replace with: "datasource": "Graphite"`); + console.error(`Error: ${filePath}`); + + process.exit(1); + } else { + console.log("✨ Grafana Dashboard linter passed"); + resolve(); + } + }); + }); +}; diff --git a/apps/hubble/scripts/linter.cjs b/apps/hubble/scripts/linter.cjs index cd202b7b23..a0f2ae606a 100644 --- a/apps/hubble/scripts/linter.cjs +++ b/apps/hubble/scripts/linter.cjs @@ -1,50 +1,20 @@ /** - * This is a custom linter that checks for the presence of a "datasource" property - * in the Grafana dashboard JSON file. If the property is present, it must be set - * to "Graphite". This is to ensure that the dashboard is not using any other - * datasource. + * Custom linters for Hubble. There are some additional things we want to check + * for that are not covered by the default linters. * - * It runs as a part of "yarn lint" + * For eg. we want to check if the Grafana dashboards are valid JSON and if the + * datasource is set to "Graphite". + * + * Add linters as .cjs files in this directory and they will be executed as a + * part of "yarn lint". */ -const fs = require("fs"); -const path = require("path"); - -const filePath = path.join(__dirname, "../grafana/grafana-dashboard.json"); - -function checkDataSource(lineArray) { - let lineNumber = 0; - - for (const line of lineArray) { - lineNumber++; +async function executeAll() { + const grafana = require("./grafanadash.cjs"); + await grafana(); - // Check if the current line contains the undesired datasource entry - if (line.includes('"datasource":') && !line.replaceAll(" ", "").includes('"datasource":"Graphite"')) { - return lineNumber; - } - } - - return null; + const clidocs = require("./clidocs.cjs"); + await clidocs(); } -fs.readFile(filePath, "utf-8", (err, data) => { - if (err) { - console.error(`Error reading ${filePath}:`, err); - process.exit(1); - } - - // Split the content into lines - const lineArray = data.split("\n"); - const errorLine = checkDataSource(lineArray); - - if (errorLine !== null) { - console.error(`line ${errorLine}: ${lineArray[errorLine - 1]}`); - console.error(`Error: "datasource" has to be "Graphite"`); - console.error(`Replace with: "datasource": "Graphite"`); - console.error(`Error: ${filePath}`); - - process.exit(1); - } else { - console.log("✨ Custom linter passed"); - } -}); +executeAll(); diff --git a/apps/hubble/src/cli.ts b/apps/hubble/src/cli.ts index fe38141004..8bcc2fc68d 100644 --- a/apps/hubble/src/cli.ts +++ b/apps/hubble/src/cli.ts @@ -29,7 +29,7 @@ import { profileGossipServer } from "./profile/gossipProfile.js"; import { initializeStatsd } from "./utils/statsd.js"; import OnChainEventStore from "./storage/stores/onChainEventStore.js"; import { startupCheck, StartupCheckStatus } from "./utils/startupCheck.js"; -import { goerli, mainnet, optimism } from "viem/chains"; +import { mainnet, optimism } from "viem/chains"; import { finishAllProgressBars } from "./utils/progressBars.js"; import { MAINNET_BOOTSTRAP_PEERS } from "./bootstrapPeers.mainnet.js"; import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts"; diff --git a/apps/hubble/www/docs/docs/cli.md b/apps/hubble/www/docs/docs/cli.md index 228ee2edaf..177161d0f2 100644 --- a/apps/hubble/www/docs/docs/cli.md +++ b/apps/hubble/www/docs/docs/cli.md @@ -28,6 +28,7 @@ Start a Hub Hubble Options: -n --network ID of the Farcaster Network (default: 3 (devnet)) -i, --id Path to the PeerId file. + --hub-operator-fid The FID of the hub operator. Optional. -c, --config Path to the config file. --db-name The name of the RocksDB instance. (default: rocks.hub._default) --admin-server-enabled Enable the admin server. (default: disabled) @@ -38,10 +39,27 @@ Ethereum Options: -m, --eth-mainnet-rpc-url RPC URL of a Mainnet ETH Node (or comma separated list of URLs) -l, --l2-rpc-url RPC URL of a Goerli Optimism Node (or comma separated list of URLs) --rank-rpcs Rank the RPCs by latency/stability and use the fastest one (default: disabled) - --fname-server-url The URL for the FName registry server (default: https://fnames.farcaster.xyz + --fname-server-url The URL for the FName registry server (default: https://fnames.farcaster.xyz) --fir-address
The address of the Farcaster ID Registry contract --first-block The block number to begin syncing events from Farcaster contracts +L2 Options: + --l2-id-registry-address The address of the L2 Farcaster ID Registry contract + --l2-key-registry-address
The address of the L2 Farcaster Key Registry contract + --l2-storage-registry-address
The address of the L2 Farcaster Storage Registry contract + --l2-resync-events Resync events from the L2 Farcaster contracts before starting (default: disabled) + --l2-first-block The block number to begin syncing events from L2 Farcaster contracts + --l2-chunk-size The number of events to fetch from L2 Farcaster contracts at a time + --l2-chain-id The chain ID of the L2 Farcaster contracts are deployed to + --l2-rent-expiry-override The storage rent expiry in seconds to use instead of the default 1 year (ONLY FOR TESTS) + +Snapshots Options: + --enable-snapshot-to-s3 Enable daily snapshots to be uploaded to S3. (default: disabled) + --s3-snapshot-bucket The S3 bucket to upload snapshots to + --disable-snapshot-sync Disable syncing from snapshots. (default: enabled) +Metrics: + --statsd-metrics-server The host to send statsd metrics to, eg "127.0.0.1:8125". (default: disabled) + Networking Options: -a, --allowed-peers Only peer with specific peer ids. (default: all peers allowed) -b, --bootstrap Peers to bootstrap gossip and sync from. (default: none) @@ -51,6 +69,7 @@ Networking Options: --announce-ip Public IP address announced to peers (default: fetched with external service) --announce-server-name Server name announced to peers, useful if SSL/TLS enabled. (default: "none") --direct-peers A list of peers for libp2p to directly peer with (default: []) + --denied-peers Do not peer with specific peer ids. (default: no peers denied) --rpc-rate-limit RPC rate limit for peers specified in rpm. Set to -1 for none. (default: 20k/min) Debugging Options: @@ -63,7 +82,8 @@ Debugging Options: --commit-lock-timeout Rocks DB commit lock timeout in milliseconds (default: 500) --commit-lock-max-pending Rocks DB commit lock max pending jobs (default: 1000) --rpc-auth Require username-password auth for RPC submit. (default: disabled) - + --disable-console-status Immediately log to STDOUT, and disable console status and progressbars. (default: disabled) + --fnr-address
The address of the Farcaster Name Registry contract -h, --help display help for command ```