-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: task to stop localnet #34
Merged
Merged
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
f1acd36
feat: task to stop localnet
fadeev edf92df
fix double cleanup
fadeev 8d12a54
rename main to localnet
fadeev 4ffa884
console log anvil args only when not empty
fadeev 88b77c1
prompt user to kill anvil
fadeev 16022d4
force kill flag
fadeev 1c33bc3
if localnet fails, clean up
fadeev 7262ed4
wip
fadeev f620bf7
format table
fadeev ccc5e77
formatting
fadeev 2253d2b
fix: when using custom block time
fadeev c7ec285
stop after init
fadeev 5d5f470
ci: run localnet workflow
fadeev 1524d74
ci: fix
fadeev 63791f9
ci: fix
fadeev e79ca20
ci: fix
fadeev 209ed0b
ci: install foundry
fadeev 1eea8b4
throw error if anvil not found
fadeev 34a822a
add install foundry back, now check should pass
fadeev 136f373
remove file check
fadeev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
name: Run Localnet | ||
|
||
on: | ||
pull_request: | ||
branches: [main] | ||
|
||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
start: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v3 | ||
|
||
- name: Setup Node.js | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: "21" | ||
registry-url: "https://registry.npmjs.org" | ||
|
||
- name: Install Foundry | ||
uses: foundry-rs/foundry-toolchain@v1 | ||
|
||
- name: Install Dependencies | ||
run: yarn | ||
|
||
- name: Start Localnet | ||
run: yarn hardhat localnet --stop-after-init |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { localnetTask } from "./localnet"; | ||
export { localnetStopTask } from "./stop"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,165 @@ | ||
import { task, types } from "hardhat/config"; | ||
import { initLocalnet } from "../../localnet/src"; | ||
import { exec } from "child_process"; | ||
import { exec, execSync } from "child_process"; | ||
import waitOn from "wait-on"; | ||
import ansis from "ansis"; | ||
import fs from "fs"; | ||
import { confirm } from "@inquirer/prompts"; | ||
|
||
const main = async (args: any) => { | ||
const port = args.port || 8545; | ||
const anvilArgs = args.anvil ? `${args.anvil}` : ""; | ||
const LOCALNET_PID_FILE = "./localnet.pid"; | ||
|
||
console.log(`Starting anvil on port ${port} with args: ${anvilArgs}`); | ||
const killProcessOnPort = async (port: number, forceKill: boolean) => { | ||
try { | ||
const output = execSync(`lsof -ti tcp:${port}`).toString().trim(); | ||
if (output) { | ||
console.log( | ||
ansis.yellow(`Port ${port} is already in use by process ${output}.`) | ||
); | ||
|
||
if (forceKill) { | ||
execSync(`kill -9 ${output}`); | ||
console.log( | ||
ansis.green(`Successfully killed process ${output} on port ${port}.`) | ||
); | ||
} else { | ||
const answer = await confirm({ | ||
message: `Do you want to kill the process running on port ${port}?`, | ||
default: true, | ||
}); | ||
|
||
if (answer) { | ||
execSync(`kill -9 ${output}`); | ||
console.log( | ||
ansis.green( | ||
`Successfully killed process ${output} on port ${port}.` | ||
) | ||
); | ||
} else { | ||
console.log(ansis.red("Process not killed. Exiting...")); | ||
process.exit(1); | ||
} | ||
} | ||
} | ||
} catch (error) { | ||
// Silently continue if no process is found or killing fails | ||
} | ||
}; | ||
|
||
const localnet = async (args: any) => { | ||
try { | ||
execSync("which anvil"); | ||
} catch (error) { | ||
console.error( | ||
ansis.red( | ||
"Error: 'anvil' not found. Please install Foundry: https://getfoundry.sh" | ||
) | ||
); | ||
process.exit(1); | ||
} | ||
|
||
await killProcessOnPort(args.port, args.forceKill); | ||
|
||
if (args.anvil !== "") | ||
console.log(`Starting anvil on port ${args.port} with args: ${args.anvil}`); | ||
|
||
const anvilProcess = exec( | ||
`anvil --auto-impersonate --port ${port} ${anvilArgs}` | ||
`anvil --auto-impersonate --port ${args.port} ${args.anvil}` | ||
); | ||
|
||
if (anvilProcess.stdout && anvilProcess.stderr) { | ||
anvilProcess.stdout.pipe(process.stdout); | ||
anvilProcess.stderr.pipe(process.stderr); | ||
} | ||
|
||
await waitOn({ resources: [`tcp:127.0.0.1:${port}`] }); | ||
await waitOn({ resources: [`tcp:127.0.0.1:${args.port}`] }); | ||
|
||
const addr = await initLocalnet(port); | ||
const cleanup = () => { | ||
console.log("\nShutting down anvil and cleaning up..."); | ||
if (anvilProcess) { | ||
anvilProcess.kill(); | ||
} | ||
if (fs.existsSync(LOCALNET_PID_FILE)) { | ||
fs.unlinkSync(LOCALNET_PID_FILE); | ||
} | ||
}; | ||
|
||
console.log(ansis.cyan` | ||
EVM Contract Addresses | ||
====================== | ||
try { | ||
const addr = await initLocalnet(args.port); | ||
|
||
Gateway EVM: ${addr.gatewayEVM} | ||
ERC-20 custody: ${addr.custodyEVM} | ||
TSS: ${addr.tssEVM} | ||
ZETA: ${addr.zetaEVM}`); | ||
// EVM Contract Addresses | ||
const evmHeader = "\nEVM Contract Addresses"; | ||
console.log(ansis.cyan(`${evmHeader}\n${"=".repeat(evmHeader.length)}`)); | ||
|
||
addr.foreignCoins | ||
.filter((coin: any) => coin.asset !== "") | ||
.forEach((coin: any) => { | ||
console.log(ansis.cyan`ERC-20 ${coin.symbol}: ${coin.asset}`); | ||
}); | ||
const evmAddresses = { | ||
"Gateway EVM": addr.gatewayEVM, | ||
"ERC-20 Custody": addr.custodyEVM, | ||
TSS: addr.tssEVM, | ||
ZETA: addr.zetaEVM, | ||
...addr.foreignCoins | ||
.filter((coin: any) => coin.asset !== "") | ||
.reduce((acc: any, coin: any) => { | ||
acc[`ERC-20 ${coin.symbol}`] = coin.asset; | ||
return acc; | ||
}, {}), | ||
}; | ||
|
||
console.log(ansis.green` | ||
ZetaChain Contract Addresses | ||
============================ | ||
console.table(evmAddresses); | ||
|
||
Gateway ZetaChain: ${addr.gatewayZetaChain} | ||
ZETA: ${addr.zetaZetaChain} | ||
Fungible module: ${addr.fungibleModuleZetaChain} | ||
System contract: ${addr.sytemContractZetaChain}`); | ||
const zetaHeader = "\nZetaChain Contract Addresses"; | ||
console.log(ansis.green(`${zetaHeader}\n${"=".repeat(zetaHeader.length)}`)); | ||
|
||
addr.foreignCoins.forEach((coin: any) => { | ||
console.log( | ||
ansis.green`ZRC-20 ${coin.symbol}: ${coin.zrc20_contract_address}` | ||
); | ||
}); | ||
const zetaAddresses = { | ||
"Gateway ZetaChain": addr.gatewayZetaChain, | ||
ZETA: addr.zetaZetaChain, | ||
"Fungible Module": addr.fungibleModuleZetaChain, | ||
"System Contract": addr.sytemContractZetaChain, | ||
...addr.foreignCoins.reduce((acc: any, coin: any) => { | ||
acc[`ZRC-20 ${coin.symbol}`] = coin.zrc20_contract_address; | ||
return acc; | ||
}, {}), | ||
}; | ||
|
||
console.table(zetaAddresses); | ||
|
||
fs.writeFileSync(LOCALNET_PID_FILE, process.pid.toString(), "utf-8"); | ||
} catch (error: any) { | ||
console.error(ansis.red`Error initializing localnet: ${error}`); | ||
cleanup(); | ||
process.exit(1); | ||
} | ||
|
||
process.on("SIGINT", () => { | ||
console.log("\nReceived Ctrl-C, shutting down anvil..."); | ||
anvilProcess.kill(); | ||
const handleExit = (signal: string) => { | ||
console.log(`\nReceived ${signal}, shutting down...`); | ||
cleanup(); | ||
process.exit(); | ||
}; | ||
|
||
process.on("SIGINT", () => handleExit("SIGINT")); | ||
process.on("SIGTERM", () => handleExit("SIGTERM")); | ||
|
||
process.on("exit", () => { | ||
console.log("Process exiting..."); | ||
}); | ||
|
||
if (args.stopAfterInit) { | ||
console.log(ansis.green("Localnet successfully initialized. Stopping...")); | ||
cleanup(); | ||
process.exit(0); | ||
} | ||
|
||
await new Promise(() => {}); | ||
}; | ||
|
||
export const localnetTask = task("localnet", "Start localnet", main) | ||
export const localnetTask = task("localnet", "Start localnet", localnet) | ||
.addOptionalParam("port", "Port to run anvil on", 8545, types.int) | ||
.addOptionalParam( | ||
"anvil", | ||
"Additional arguments to pass to anvil", | ||
"", | ||
types.string | ||
) | ||
.addFlag("forceKill", "Force kill any process on the port without prompting") | ||
.addFlag( | ||
"stopAfterInit", | ||
"Stop the localnet after successful initialization" | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { task } from "hardhat/config"; | ||
import fs from "fs"; | ||
import ansis from "ansis"; | ||
|
||
const LOCALNET_PID_FILE = "./localnet.pid"; | ||
|
||
const localnetStop = async (args: any) => { | ||
if (!fs.existsSync(LOCALNET_PID_FILE)) { | ||
console.log(ansis.red("Localnet is not running or PID file is missing.")); | ||
return; | ||
} | ||
|
||
const pid = fs.readFileSync(LOCALNET_PID_FILE, "utf-8").trim(); | ||
try { | ||
process.kill(Number(pid)); | ||
console.log(ansis.green(`Successfully stopped localnet (PID: ${pid})`)); | ||
|
||
// Unlink PID file only if it exists | ||
if (fs.existsSync(LOCALNET_PID_FILE)) { | ||
fs.unlinkSync(LOCALNET_PID_FILE); | ||
console.log(ansis.green(`PID file ${LOCALNET_PID_FILE} removed.`)); | ||
} | ||
} catch (err) { | ||
console.error(ansis.red(`Failed to stop localnet: ${err}`)); | ||
} | ||
}; | ||
|
||
export const localnetStopTask = task( | ||
"localnet-stop", | ||
"Stop localnet", | ||
localnetStop | ||
); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point we're sure the file exists, right? As we are reading from it before the try-catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right 👍 136f373