-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* basic scaffolding for github action * Add build action step * Get rid of runtime dep on bun * Find pending spells * Extract common logic from CLI and action * Expose public tenderly URLs * Post github comment
- Loading branch information
Showing
18 changed files
with
194 additions
and
52 deletions.
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
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,24 @@ | ||
name: Spell Caster | ||
description: Spark Spell Caster | ||
|
||
inputs: | ||
github-token: | ||
description: 'Github token is needed to post comments on the PR' | ||
required: true | ||
TENDERLY_ACCOUNT: | ||
description: 'Tenderly credentials' | ||
required: true | ||
TENDERLY_PROJECT: | ||
description: 'Tenderly credentials' | ||
required: true | ||
TENDERLY_API_KEY: | ||
description: 'Tenderly credentials' | ||
required: true | ||
|
||
outputs: | ||
time: | ||
description: The time we greeted you | ||
|
||
runs: | ||
using: node20 | ||
main: out/action.js |
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 |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import assert from 'node:assert' | ||
import core from '@actions/core' | ||
import github from '@actions/github' | ||
import { createCommentOrUpdate } from '@superactions/comment' | ||
import dedent from 'dedent' | ||
import { markdownTable } from 'markdown-table' | ||
import { ForkAndExecuteSpellReturn, forkAndExecuteSpell } from '..' | ||
import { getConfig } from '../config' | ||
import { getRequiredGithubInput } from '../config/environments/action' | ||
import { findPendingSpells } from '../spells/findPendingSpells' | ||
|
||
async function main(): Promise<void> { | ||
const config = getConfig(getRequiredGithubInput) | ||
|
||
const allPendingSpellNames = findPendingSpells(process.cwd()) | ||
core.info(`Pending spells: ${allPendingSpellNames.join(', ')}`) | ||
|
||
const results = await Promise.all(allPendingSpellNames.map((spellName) => forkAndExecuteSpell(spellName, config))) | ||
|
||
await postGithubComment(results) | ||
|
||
core.info(`Results: ${JSON.stringify(results)}`) | ||
} | ||
|
||
await main().catch((error) => { | ||
core.setFailed(error) | ||
}) | ||
|
||
const uniqueAppId = 'spark-spells-action' | ||
async function postGithubComment(results: ForkAndExecuteSpellReturn[]): Promise<void> { | ||
const now = new Date().toISOString() | ||
const sha = getPrSha() | ||
const table = [ | ||
['Spell', 'App URL', 'RPC URL'], | ||
...results.map((result) => [result.spellName, `[🎇 App](${result.appUrl})`, `[🌎 RPC](${result.forkRpc})`]), | ||
] | ||
const message = dedent` | ||
## Spell Caster | ||
Inspect the impact of spell execution on forked networks: | ||
${markdownTable(table)} | ||
<sub>Deployed from ${sha} on ${now}</sub> | ||
` | ||
await createCommentOrUpdate({ githubToken: core.getInput('github-token'), message, uniqueAppId: uniqueAppId }) | ||
} | ||
|
||
function getPrSha(): string { | ||
const context = github.context | ||
|
||
assert(context.eventName === 'pull_request', 'This action can only be run on pull requests') | ||
|
||
return context.payload.pull_request!.head.sha | ||
} |
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,19 @@ | ||
import assert from 'node:assert' | ||
import { forkAndExecuteSpell } from '..' | ||
import { getConfig } from '../config' | ||
import { getRequiredShellEnv } from '../config/environments/cli' | ||
|
||
async function main(spellName?: string) { | ||
assert(spellName, 'Pass spell name as an argument ex. SparkEthereum_20240627') | ||
|
||
const config = getConfig(getRequiredShellEnv) | ||
|
||
console.log(`Executing spell ${spellName}`) | ||
const { forkRpc, appUrl } = await forkAndExecuteSpell(spellName, config) | ||
|
||
console.log(`Fork Network RPC: ${forkRpc}`) | ||
console.log(`Spark App URL: ${appUrl}`) | ||
} | ||
|
||
const arg1 = process.argv[2] | ||
await main(arg1) |
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,8 @@ | ||
import assert from 'node:assert' | ||
import core from '@actions/core' | ||
|
||
export function getRequiredGithubInput(key: string): string { | ||
const value = core.getInput(key) | ||
assert(value, `Missing required github input: ${key}`) | ||
return value | ||
} |
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,43 +1,44 @@ | ||
import assert from 'node:assert' | ||
import { zeroAddress } from 'viem' | ||
import { getConfig } from './config' | ||
import { executeSpell } from './executeSpell' | ||
import { Config } from './config' | ||
import { EthereumClient } from './periphery/ethereum' | ||
import { deployContract } from './periphery/forge' | ||
import { buildAppUrl } from './periphery/spark-app' | ||
import { createTenderlyVNet, getRandomChainId } from './periphery/tenderly' | ||
import { deployContract } from './utils/forge' | ||
import { executeSpell } from './spells/executeSpell' | ||
import { getChainIdFromSpellName } from './utils/getChainIdFromSpellName' | ||
|
||
const deployer = zeroAddress | ||
|
||
async function main(spellName?: string) { | ||
assert(spellName, 'Pass spell name as an argument ex. SparkEthereum_20240627') | ||
export interface ForkAndExecuteSpellReturn { | ||
spellName: string | ||
originChainId: number | ||
forkRpc: string | ||
forkChainId: number | ||
appUrl: string | ||
} | ||
|
||
const config = getConfig() | ||
export async function forkAndExecuteSpell(spellName: string, config: Config): Promise<ForkAndExecuteSpellReturn> { | ||
const originChainId = getChainIdFromSpellName(spellName) | ||
const chain = config.networks[originChainId] | ||
assert(chain, `Chain not found for chainId: ${originChainId}`) | ||
const forkChainId = getRandomChainId() | ||
|
||
console.log(`Executing spell ${spellName} on ${chain.name} (chainId=${originChainId})`) | ||
|
||
const rpc = await createTenderlyVNet({ | ||
account: config.tenderly.account, | ||
apiKey: config.tenderly.apiKey, | ||
project: config.tenderly.project, | ||
originChainId: originChainId, | ||
forkChainId, | ||
}) | ||
const ethereumClient = new EthereumClient(rpc, forkChainId, deployer) | ||
const ethereumClient = new EthereumClient(rpc.adminRpcUrl, forkChainId, config.deployer) | ||
|
||
const spellAddress = await deployContract(spellName, rpc, deployer) | ||
const spellAddress = await deployContract(spellName, rpc.adminRpcUrl, config.deployer) | ||
|
||
await executeSpell({ spellAddress, network: chain, ethereumClient }) | ||
|
||
console.log(`Fork Network RPC: ${rpc}`) | ||
console.log(`Spark App URL: ${buildAppUrl({ rpc, originChainId })}`) | ||
return { | ||
spellName, | ||
originChainId, | ||
forkRpc: rpc.publicRpcUrl, | ||
forkChainId, | ||
appUrl: buildAppUrl({ rpc: rpc.publicRpcUrl, originChainId }), | ||
} | ||
} | ||
|
||
const arg1 = process.argv[2] | ||
|
||
await main(arg1) |
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
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 |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { describe, expect, test } from 'bun:test' | ||
import { getFilenameWithoutExtension } from './findPendingSpells' | ||
|
||
describe(getFilenameWithoutExtension.name, () => { | ||
test('gets filename without extension from a full path', () => { | ||
const fullPath = '/path/to/file.sol' | ||
const result = getFilenameWithoutExtension(fullPath) | ||
expect(result).toBe('file') | ||
}) | ||
}) |
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,22 @@ | ||
import * as fs from 'node:fs' | ||
import * as path from 'node:path' | ||
import * as glob from 'glob' | ||
|
||
export function findPendingSpells(projectRootPath: string): string[] { | ||
const proposalsDirPath = path.join(projectRootPath, 'src/proposals') | ||
|
||
if (!fs.existsSync(proposalsDirPath)) { | ||
throw new Error(`Directory not found: ${proposalsDirPath}`) | ||
} | ||
|
||
const spellsAndTests = glob.globSync('**/*.sol', { cwd: proposalsDirPath, absolute: true }) | ||
|
||
const spellPaths = spellsAndTests.filter((path) => !path.includes('.t.sol')) | ||
|
||
return spellPaths.map(getFilenameWithoutExtension) | ||
} | ||
|
||
export function getFilenameWithoutExtension(fullPath: string): string { | ||
const parsedPath = path.parse(fullPath) | ||
return parsedPath.name | ||
} |