Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,21 @@ jobs:
name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Show Forge version
run: |
forge --version
run: forge --version

- name: Run Forge fmt
run: |
forge fmt --check
id: fmt
run: forge fmt --check

- name: Run Forge build
run: |
forge build --sizes
id: build
run: forge build --sizes

- name: Run Forge tests
run: |
forge test -vvv
id: test
run: forge test -vvv
19 changes: 19 additions & 0 deletions crates/forge/assets/vyper/CounterTemplate.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script} from "forge-std/Script.sol";
import {ICounter} from "../src/ICounter.sol";

contract CounterScript is Script {
ICounter public counter;

function setUp() public {}

function run() public {
vm.startBroadcast();

counter = ICounter(deployCode("src/Counter.vy"));

vm.stopBroadcast();
}
}
24 changes: 24 additions & 0 deletions crates/forge/assets/vyper/CounterTemplate.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test} from "forge-std/Test.sol";
import {ICounter} from "../src/ICounter.sol";

contract CounterTest is Test {
ICounter public counter;

function setUp() public {
counter = ICounter(deployCode("src/Counter.vy"));
counter.setNumber(0);
}

function test_Increment() public {
counter.increment();
assertEq(counter.number(), 1);
}

function testFuzz_SetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
}
11 changes: 11 additions & 0 deletions crates/forge/assets/vyper/CounterTemplate.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# pragma version ~=0.4.3

number: public(uint256)

@external
def setNumber(newNumber: uint256):
self.number = newNumber

@external
def increment():
self.number += 1
8 changes: 8 additions & 0 deletions crates/forge/assets/vyper/ICounterTemplate.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

interface ICounter {
function number() external view returns (uint256);
function setNumber(uint256 newNumber) external;
function increment() external;
}
44 changes: 44 additions & 0 deletions crates/forge/assets/vyper/workflowTemplate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
push:
pull_request:
workflow_dispatch:

env:
FOUNDRY_PROFILE: ci

jobs:
check:
name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Show Forge version
run: forge --version

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.x"

- name: Install Vyper
run: pip install git+https://github.com/vyperlang/vyper.git@v0.4.3

- name: Show the Vyper version
run: vyper --version

- name: Run Forge fmt
run: forge fmt --check

- name: Run Forge build
run: forge build --sizes

- name: Run Forge tests
run: forge test -vvv
64 changes: 51 additions & 13 deletions crates/forge/src/cmd/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ pub struct InitArgs {
#[arg(long, conflicts_with = "template")]
pub vscode: bool,

/// Initialize a Vyper project template.
#[arg(long, conflicts_with = "template")]
pub vyper: bool,

/// Use the parent git repository instead of initializing a new one.
/// Only valid if the target is in a git repository.
#[arg(long, conflicts_with = "template")]
Expand All @@ -48,7 +52,8 @@ pub struct InitArgs {

impl InitArgs {
pub fn run(self) -> Result<()> {
let Self { root, template, branch, install, offline, force, vscode, use_parent_git } = self;
let Self { root, template, branch, install, offline, force, vscode, use_parent_git, vyper } =
self;
let DependencyInstallOpts { shallow, no_git, commit } = install;

// create the root dir if it does not exist
Expand Down Expand Up @@ -123,15 +128,43 @@ impl InitArgs {
let script = root.join("script");
fs::create_dir_all(&script)?;

// write the contract file
let contract_path = src.join("Counter.sol");
fs::write(contract_path, include_str!("../../assets/CounterTemplate.sol"))?;
// write the tests
let contract_path = test.join("Counter.t.sol");
fs::write(contract_path, include_str!("../../assets/CounterTemplate.t.sol"))?;
// write the script
let contract_path = script.join("Counter.s.sol");
fs::write(contract_path, include_str!("../../assets/CounterTemplate.s.sol"))?;
if vyper {
// write the contract file
let contract_path = src.join("Counter.vy");
fs::write(contract_path, include_str!("../../assets/vyper/CounterTemplate.vy"))?;
let interface_path = src.join("ICounter.sol");
fs::write(interface_path, include_str!("../../assets/vyper/ICounterTemplate.sol"))?;

// write the tests
let contract_path = test.join("Counter.t.sol");
fs::write(contract_path, include_str!("../../assets/vyper/CounterTemplate.t.sol"))?;

// write the script
let contract_path = script.join("Counter.s.sol");
fs::write(contract_path, include_str!("../../assets/vyper/CounterTemplate.s.sol"))?;
} else {
// write the contract file
let contract_path = src.join("Counter.sol");
fs::write(
contract_path,
include_str!("../../assets/solidity/CounterTemplate.sol"),
)?;

// write the tests
let contract_path = test.join("Counter.t.sol");
fs::write(
contract_path,
include_str!("../../assets/solidity/CounterTemplate.t.sol"),
)?;

// write the script
let contract_path = script.join("Counter.s.sol");
fs::write(
contract_path,
include_str!("../../assets/solidity/CounterTemplate.s.sol"),
)?;
}

// Write the default README file
let readme_path = root.join("README.md");
fs::write(readme_path, include_str!("../../assets/README.md"))?;
Expand All @@ -146,7 +179,7 @@ impl InitArgs {

// set up the repo
if !no_git {
init_git_repo(git, commit, use_parent_git)?;
init_git_repo(git, commit, use_parent_git, vyper)?;
}

// install forge-std
Expand Down Expand Up @@ -177,7 +210,7 @@ impl InitArgs {
/// Creates `.gitignore` and `.github/workflows/test.yml`, if they don't exist already.
///
/// Commits everything in `root` if `commit` is true.
fn init_git_repo(git: Git<'_>, commit: bool, use_parent_git: bool) -> Result<()> {
fn init_git_repo(git: Git<'_>, commit: bool, use_parent_git: bool, vyper: bool) -> Result<()> {
// `git init`
if !git.is_in_repo()? || (!use_parent_git && !git.is_repo_root()?) {
git.init()?;
Expand All @@ -193,7 +226,12 @@ fn init_git_repo(git: Git<'_>, commit: bool, use_parent_git: bool) -> Result<()>
let workflow = git.root.join(".github/workflows/test.yml");
if !workflow.exists() {
fs::create_dir_all(workflow.parent().unwrap())?;
fs::write(workflow, include_str!("../../assets/workflowTemplate.yml"))?;

if vyper {
fs::write(workflow, include_str!("../../assets/vyper/workflowTemplate.yml"))?;
} else {
fs::write(workflow, include_str!("../../assets/solidity/workflowTemplate.yml"))?;
}
}

// commit everything
Expand Down
57 changes: 57 additions & 0 deletions crates/forge/tests/cli/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,63 @@ forgetest!(can_clone_keep_directory_structure, |prj, cmd| {
let _config: BasicConfig = parse_with_profile(&s).unwrap().unwrap().1;
});

// checks that `forge init` works.
forgetest!(can_init_project, |prj, cmd| {
prj.wipe();

cmd.args(["init"]).arg(prj.root()).assert_success().stdout_eq(str![[r#"
Initializing [..]...
Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None)
Installed forge-std[..]
Initialized forge project

"#]]);

assert!(prj.root().join("foundry.toml").exists());
assert!(prj.root().join("lib/forge-std").exists());

assert!(prj.root().join("src").exists());
assert!(prj.root().join("src").join("Counter.sol").exists());

assert!(prj.root().join("test").exists());
assert!(prj.root().join("test").join("Counter.t.sol").exists());

assert!(prj.root().join("script").exists());
assert!(prj.root().join("script").join("Counter.s.sol").exists());

assert!(prj.root().join(".github").join("workflows").exists());
assert!(prj.root().join(".github").join("workflows").join("test.yml").exists());
});

// checks that `forge init --vyper` works.
forgetest!(can_init_vyper_project, |prj, cmd| {
prj.wipe();

cmd.args(["init", "--vyper"]).arg(prj.root()).assert_success().stdout_eq(str![[r#"
Initializing [..]...
Installing forge-std in [..] (url: Some("https://github.com/foundry-rs/forge-std"), tag: None)
Installed forge-std[..]
Initialized forge project

"#]]);

assert!(prj.root().join("foundry.toml").exists());
assert!(prj.root().join("lib/forge-std").exists());

assert!(prj.root().join("src").exists());
assert!(prj.root().join("src").join("Counter.vy").exists());
assert!(prj.root().join("src").join("ICounter.sol").exists());

assert!(prj.root().join("test").exists());
assert!(prj.root().join("test").join("Counter.t.sol").exists());

assert!(prj.root().join("script").exists());
assert!(prj.root().join("script").join("Counter.s.sol").exists());

assert!(prj.root().join(".github").join("workflows").exists());
assert!(prj.root().join(".github").join("workflows").join("test.yml").exists());
});

// checks that clone works with raw src containing `node_modules`
// <https://github.com/foundry-rs/foundry/issues/10115>
forgetest!(can_clone_with_node_modules, |prj, cmd| {
Expand Down
Loading