From 061242ea485a6fc24dfded91dfabbd5a753e8a66 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Tue, 11 Aug 2026 16:28:57 -0400 Subject: [PATCH 1/6] change(ci): verifies that the committed Go FFI binding --- .github/workflows/go-lint.yml | 9 ++++++--- CHANGELOG.md | 2 ++ interop-tests/build.rs | 28 ++++++++++++++++++---------- mise.toml | 8 ++++++++ 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index 95f2217d575..872d0cc2d76 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -18,6 +18,7 @@ on: - "go.work" - "mise.toml" - "f3-sidecar/**" + - "interop-tests/*.rs" - "interop-tests/src/tests/**" push: branches: @@ -27,7 +28,7 @@ on: - "go.work" - "mise.toml" - "f3-sidecar/**" - - "interop-tests/src/tests/**" + - "interop-tests/**" jobs: # Merge groups don't support path filters, so detect the relevant changes @@ -48,7 +49,7 @@ jobs: - 'go.work' - 'mise.toml' - 'f3-sidecar/**' - - 'interop-tests/src/tests/**' + - "interop-tests/**" lint-go: name: Go lint checks needs: changes @@ -57,4 +58,6 @@ jobs: steps: - uses: actions/checkout@v7 - uses: jdx/mise-action@v4 - - run: mise lint:golang + - run: | + mise lint:go-ffi + mise lint:golang \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 45ac5b99b0b..2c512c5b34b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ ### Changed +- [#7467](https://github.com/ChainSafe/forest/issues/7467): CI now verifies that the committed Go FFI binding `interop-tests/src/tests/go_app/ffi_gen.go` stays in sync with `go_ffi.rs` through the `FOREST_REGENERATE_GO_FFI` environment variable, included `mise run lint:go-ffi`. + ### Removed ### Fixed diff --git a/interop-tests/build.rs b/interop-tests/build.rs index a01980947b1..db5711d65dd 100644 --- a/interop-tests/build.rs +++ b/interop-tests/build.rs @@ -3,18 +3,26 @@ fn main() { println!("cargo::rerun-if-changed=src/tests/go_app"); + println!("cargo::rerun-if-changed=src/tests/go_ffi.rs"); + println!("cargo::rerun-if-env-changed=FOREST_REGENERATE_GO_FFI"); + unsafe { std::env::set_var("GOWORK", "off"); std::env::set_var("GOFLAGS", "-tags=netgo"); } - rust2go::Builder::default() - .with_go_src("./src/tests/go_app") - // the generated Go file has been committed to the git repository, - // uncomment to regenerate the code locally - // .with_regen_arg(rust2go::RegenArgs { - // src: "./src/tests/go_ffi.rs".into(), - // dst: "./src/tests/go_app/ffi_gen.go".into(), - // ..Default::default() - // }) - .build(); + + let mut builder = rust2go::Builder::default() + .with_go_src("./src/tests/go_app"); + + // the generated Go file has been committed to the git repository + // set the var to regenerate the file, CI sets this var to verify freshness. + if std::env::var_os("FOREST_REGENERATE_GO_FFI").is_some() { + builder = builder.with_regen_arg(rust2go::RegenArgs { + src: "./src/tests/go_ffi.rs".into(), + dst: "./src/tests/go_app/ffi_gen.go".into(), + ..Default::default() + }) + } + + builder.build(); } diff --git a/mise.toml b/mise.toml index fcbcf4e5698..005f04999f6 100644 --- a/mise.toml +++ b/mise.toml @@ -99,6 +99,14 @@ run = ''' golangci-lint run ./f3-sidecar ./interop-tests/src/tests/go_app ''' +[tasks."lint:go-ffi"] +description = "Verify Go FFI generation is in sync with go_ffi.rs." +run = ''' +FOREST_REGENERATE_GO_FFI=1 cargo check -p forest-interop-tests --tests --profile quick +git diff --exit-code -- interop-tests/src/tests/go_app/ffi_gen.go \ + || (echo "ffi_gen.go is stale — run 'mise lint:go-ffi' and commit the result"; false) +''' + [tasks."lint:ruby"] description = "Lint Ruby code using rubocop." tools.ruby = "latest" From 702ce1c06b9dd6dfea8a485f3074c175659339aa Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Tue, 11 Aug 2026 16:35:50 -0400 Subject: [PATCH 2/6] chore: change runner and installs rust2go deps --- .github/workflows/go-lint.yml | 14 +++++++------- mise.toml | 6 ++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index 872d0cc2d76..825057acd9c 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -18,8 +18,7 @@ on: - "go.work" - "mise.toml" - "f3-sidecar/**" - - "interop-tests/*.rs" - - "interop-tests/src/tests/**" + - "interop-tests/**" push: branches: - main @@ -49,15 +48,16 @@ jobs: - 'go.work' - 'mise.toml' - 'f3-sidecar/**' - - "interop-tests/**" + - 'interop-tests/**' lint-go: name: Go lint checks needs: changes if: ${{ needs.changes.outputs.changesFound == 'true' || github.event_name == 'workflow_dispatch' }} - runs-on: ubuntu-slim + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v7 - uses: jdx/mise-action@v4 - - run: | - mise lint:go-ffi - mise lint:golang \ No newline at end of file + - name: Apt Dependencies + run: sudo apt-get install -y libclang-dev # required by rust2go's bindgen + - run: mise lint:go-ffi + - run: mise lint:golang diff --git a/mise.toml b/mise.toml index 005f04999f6..c1a2b535aae 100644 --- a/mise.toml +++ b/mise.toml @@ -100,9 +100,11 @@ golangci-lint run ./f3-sidecar ./interop-tests/src/tests/go_app ''' [tasks."lint:go-ffi"] -description = "Verify Go FFI generation is in sync with go_ffi.rs." +description = "Verify the committed Go FFI binding is in sync with go_ffi.rs." run = ''' -FOREST_REGENERATE_GO_FFI=1 cargo check -p forest-interop-tests --tests --profile quick +# Checking the (empty) lib target is enough to run `build.rs`, which does the +# regeneration. Adding `--tests` would compile the whole Forest node instead. +FOREST_REGENERATE_GO_FFI=1 cargo check -p forest-interop-tests --profile quick git diff --exit-code -- interop-tests/src/tests/go_app/ffi_gen.go \ || (echo "ffi_gen.go is stale — run 'mise lint:go-ffi' and commit the result"; false) ''' From df711c279c7fdfe83e7959f7c07d4a3426c49b8b Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Tue, 11 Aug 2026 20:47:36 -0400 Subject: [PATCH 3/6] chore: resolve simple rust lint diff --- interop-tests/build.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interop-tests/build.rs b/interop-tests/build.rs index db5711d65dd..ac5d0094eba 100644 --- a/interop-tests/build.rs +++ b/interop-tests/build.rs @@ -11,8 +11,7 @@ fn main() { std::env::set_var("GOFLAGS", "-tags=netgo"); } - let mut builder = rust2go::Builder::default() - .with_go_src("./src/tests/go_app"); + let mut builder = rust2go::Builder::default().with_go_src("./src/tests/go_app"); // the generated Go file has been committed to the git repository // set the var to regenerate the file, CI sets this var to verify freshness. From d77c7dfe36987edbd1353c47267d946e07c46801 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Wed, 12 Aug 2026 10:46:08 -0400 Subject: [PATCH 4/6] chore: include `FOREST_REGENERATE_GO_FFI` for `build.rs` and include it on docs --- .github/workflows/go-lint.yml | 15 ++++++++++-- AGENTS.md | 1 + CHANGELOG.md | 2 +- build.rs | 28 +++++++++++++--------- docs/docs/users/reference/env_variables.md | 6 +++++ f3-sidecar/README.md | 2 ++ interop-tests/build.rs | 11 +++++++-- mise.toml | 10 ++++---- src/f3/go_ffi.rs | 6 ++--- 9 files changed, 56 insertions(+), 25 deletions(-) diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index 825057acd9c..f17ff4a648a 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -19,6 +19,8 @@ on: - "mise.toml" - "f3-sidecar/**" - "interop-tests/**" + - "build.rs" + - "src/f3/go_ffi.rs" push: branches: - main @@ -28,6 +30,8 @@ on: - "mise.toml" - "f3-sidecar/**" - "interop-tests/**" + - "build.rs" + - "src/f3/go_ffi.rs" jobs: # Merge groups don't support path filters, so detect the relevant changes @@ -49,15 +53,22 @@ jobs: - 'mise.toml' - 'f3-sidecar/**' - 'interop-tests/**' + - 'build.rs' + - 'src/f3/go_ffi.rs' lint-go: name: Go lint checks needs: changes if: ${{ needs.changes.outputs.changesFound == 'true' || github.event_name == 'workflow_dispatch' }} - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v7 - uses: jdx/mise-action@v4 - name: Apt Dependencies - run: sudo apt-get install -y libclang-dev # required by rust2go's bindgen + uses: nick-fields/retry@v4 + with: + timeout_minutes: 5 + max_attempts: 3 + command: | + sudo apt-get install -y libclang-dev # required by rust2go's bindgen - run: mise lint:go-ffi - run: mise lint:golang diff --git a/AGENTS.md b/AGENTS.md index 101e3826c21..3f3b743f519 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -354,6 +354,7 @@ lldb target/debugging/forest - `RUST_LOG` - Logging configuration (e.g., `debug`, `forest=trace`) - `FULLNODE_API_INFO` - RPC endpoint and authentication token - `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT` - Disable F3 sidecar build (for debugging profile) +- `FOREST_REGENERATE_GO_FFI` - when set to `1` forces the regeneration of all Go FFI bindings based on their current Rust definitions, MUST USE when changing any `go_ffi.rs` ## Build Profiles diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c512c5b34b..e9c53af0315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ ### Changed -- [#7467](https://github.com/ChainSafe/forest/issues/7467): CI now verifies that the committed Go FFI binding `interop-tests/src/tests/go_app/ffi_gen.go` stays in sync with `go_ffi.rs` through the `FOREST_REGENERATE_GO_FFI` environment variable, included `mise run lint:go-ffi`. +- [#7467](https://github.com/ChainSafe/forest/issues/7467): CI now verifies that the committed Go FFI bindings `interop-tests/src/tests/go_app/ffi_gen.go` and `f3-sidecar/ffi_gen.go` stay in sync with their `go_ffi.rs` sources through the `FOREST_REGENERATE_GO_FFI` environment variable, included `mise run lint:go-ffi`. ### Removed diff --git a/build.rs b/build.rs index 0b9e229499f..ab56c66b714 100644 --- a/build.rs +++ b/build.rs @@ -33,17 +33,23 @@ fn main() { // See std::env::set_var("GOFLAGS", "-tags=netgo"); } - rust2go::Builder::default() - .with_go_src("./f3-sidecar") - // the generated Go file has been committed to the git repository, - // uncomment to regenerate the code locally - // .with_regen_arg(rust2go::RegenArgs { - // src: "./src/f3/go_ffi.rs".into(), - // dst: "./f3-sidecar/ffi_gen.go".into(), - // without_main: true, - // ..Default::default() - // }) - .build(); + println!("cargo:rerun-if-changed=src/f3/go_ffi.rs"); + println!("cargo:rerun-if-env-changed=FOREST_REGENERATE_GO_FFI"); + + let mut builder = rust2go::Builder::default().with_go_src("./f3-sidecar"); + + // the generated Go file has been committed to the git repository + // set the var to regenerate the file + if is_env_truthy("FOREST_REGENERATE_GO_FFI") { + builder = builder.with_regen_arg(rust2go::RegenArgs { + src: "./src/f3/go_ffi.rs".into(), + dst: "./f3-sidecar/ffi_gen.go".into(), + without_main: true, + ..Default::default() + }) + } + + builder.build(); } rpc_regression_tests_gen(); diff --git a/docs/docs/users/reference/env_variables.md b/docs/docs/users/reference/env_variables.md index c99c51f95d0..ed27bad9b2f 100644 --- a/docs/docs/users/reference/env_variables.md +++ b/docs/docs/users/reference/env_variables.md @@ -88,6 +88,12 @@ the binary. By default, the Go f3-sidecar is built and linked into Forest binary unless environment variable `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT=1` is set. +### `FOREST_REGENERATE_GO_FFI` + +Forces the regeneration of all Go FFI bindings in the project when set `FOREST_REGENERATE_GO_FFI=1`. + +Used in the CI checks to ensure the current commited Go FFI bindings are in sync with their respective Rust definitions + ### `FOREST_DB_DEV_MODE` By default, Forest will create a database of its current version or try to diff --git a/f3-sidecar/README.md b/f3-sidecar/README.md index 84f5cb3ed0a..03470d79090 100644 --- a/f3-sidecar/README.md +++ b/f3-sidecar/README.md @@ -64,3 +64,5 @@ environment variable `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT=1` is set. F3 sidecar is not started by default, set `FOREST_F3_SIDECAR_FFI_ENABLED=1` to opt in. + +To generate the Go F3-sidecar FFI bindings set `FOREST_REGENERATE_GO_FFI=1` otherwise it will use current/already generated ones diff --git a/interop-tests/build.rs b/interop-tests/build.rs index ac5d0094eba..4c1ff392a5c 100644 --- a/interop-tests/build.rs +++ b/interop-tests/build.rs @@ -10,12 +10,12 @@ fn main() { std::env::set_var("GOWORK", "off"); std::env::set_var("GOFLAGS", "-tags=netgo"); } - + let mut builder = rust2go::Builder::default().with_go_src("./src/tests/go_app"); // the generated Go file has been committed to the git repository // set the var to regenerate the file, CI sets this var to verify freshness. - if std::env::var_os("FOREST_REGENERATE_GO_FFI").is_some() { + if is_env_truthy("FOREST_REGENERATE_GO_FFI") { builder = builder.with_regen_arg(rust2go::RegenArgs { src: "./src/tests/go_ffi.rs".into(), dst: "./src/tests/go_app/ffi_gen.go".into(), @@ -25,3 +25,10 @@ fn main() { builder.build(); } + +fn is_env_truthy(env: &str) -> bool { + std::env::var(env) + .ok() + .map(|var| matches!(var.to_lowercase().as_str(), "1" | "true" | "yes" | "_yes_")) + .unwrap_or_default() +} diff --git a/mise.toml b/mise.toml index c1a2b535aae..0ada261a634 100644 --- a/mise.toml +++ b/mise.toml @@ -100,13 +100,13 @@ golangci-lint run ./f3-sidecar ./interop-tests/src/tests/go_app ''' [tasks."lint:go-ffi"] -description = "Verify the committed Go FFI binding is in sync with go_ffi.rs." +description = "Verify the committed Go FFI bindings are in sync with their go_ffi.rs sources." run = ''' -# Checking the (empty) lib target is enough to run `build.rs`, which does the -# regeneration. Adding `--tests` would compile the whole Forest node instead. +# Checking the (empty) lib target is enough to run `build.rs` FOREST_REGENERATE_GO_FFI=1 cargo check -p forest-interop-tests --profile quick -git diff --exit-code -- interop-tests/src/tests/go_app/ffi_gen.go \ - || (echo "ffi_gen.go is stale — run 'mise lint:go-ffi' and commit the result"; false) +FOREST_REGENERATE_GO_FFI=1 cargo check -p forest-filecoin --profile quick +git diff --exit-code -- interop-tests/src/tests/go_app/ffi_gen.go f3-sidecar/ffi_gen.go \ + || (echo "a committed ffi_gen.go is stale — run 'mise lint:go-ffi' and commit the result"; false) ''' [tasks."lint:ruby"] diff --git a/src/f3/go_ffi.rs b/src/f3/go_ffi.rs index 35c3bbec078..52c51105721 100644 --- a/src/f3/go_ffi.rs +++ b/src/f3/go_ffi.rs @@ -1,8 +1,6 @@ // Copyright 2019-2026 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::prelude::*; - pub mod binding { #![allow(warnings)] #![allow(clippy::indexing_slicing)] @@ -16,8 +14,8 @@ pub trait GoF3Node { jwt: String, f3_rpc_endpoint: String, initial_power_table: String, - bootstrap_epoch: ChainEpoch, - finality: ChainEpoch, + bootstrap_epoch: i64, + finality: i64, f3_root: String, ) -> bool; From ff1592ffdc1500db6b184d53663f920479206607 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Wed, 12 Aug 2026 10:50:13 -0400 Subject: [PATCH 5/6] chore: fix misspelling at docs --- docs/docs/users/reference/env_variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/users/reference/env_variables.md b/docs/docs/users/reference/env_variables.md index ed27bad9b2f..a0813596594 100644 --- a/docs/docs/users/reference/env_variables.md +++ b/docs/docs/users/reference/env_variables.md @@ -92,7 +92,7 @@ variable `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT=1` is set. Forces the regeneration of all Go FFI bindings in the project when set `FOREST_REGENERATE_GO_FFI=1`. -Used in the CI checks to ensure the current commited Go FFI bindings are in sync with their respective Rust definitions +Used in the CI checks to ensure the current tracked Go FFI bindings are in sync with their respective Rust definitions ### `FOREST_DB_DEV_MODE` From 587ddb4716150adf9911903f117ff76f5110de9b Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Wed, 12 Aug 2026 14:18:27 -0400 Subject: [PATCH 6/6] chore: include warn when `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT` is set and `FOREST_REGENERATE_GO_FFI` is also set --- build.rs | 11 ++++++++++- docs/docs/users/reference/env_variables.md | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index ab56c66b714..344269476b5 100644 --- a/build.rs +++ b/build.rs @@ -19,6 +19,16 @@ fn main() { // whitelist the cfg for cargo clippy println!("cargo::rustc-check-cfg=cfg(f3sidecar)"); + println!("cargo:rerun-if-env-changed=FOREST_REGENERATE_GO_FFI"); + if is_env_truthy("FOREST_REGENERATE_GO_FFI") + && (is_docs_rs() || is_env_truthy("FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT")) + { + println!( + "cargo:warning=FOREST_REGENERATE_GO_FFI has no effect on the f3-sidecar binding: \ + the build that regenerates it is skipped by FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT or DOCS_RS" + ); + } + // Do not build f3-sidecar on docs.rs publishing // No proper version of Go compiler is available. println!("cargo:rerun-if-env-changed=FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT"); @@ -34,7 +44,6 @@ fn main() { std::env::set_var("GOFLAGS", "-tags=netgo"); } println!("cargo:rerun-if-changed=src/f3/go_ffi.rs"); - println!("cargo:rerun-if-env-changed=FOREST_REGENERATE_GO_FFI"); let mut builder = rust2go::Builder::default().with_go_src("./f3-sidecar"); diff --git a/docs/docs/users/reference/env_variables.md b/docs/docs/users/reference/env_variables.md index a0813596594..500ed39a362 100644 --- a/docs/docs/users/reference/env_variables.md +++ b/docs/docs/users/reference/env_variables.md @@ -90,9 +90,9 @@ variable `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT=1` is set. ### `FOREST_REGENERATE_GO_FFI` -Forces the regeneration of all Go FFI bindings in the project when set `FOREST_REGENERATE_GO_FFI=1`. +Forces the regeneration of all Go FFI bindings in the project when `FOREST_REGENERATE_GO_FFI=1` is set. -Used in the CI checks to ensure the current tracked Go FFI bindings are in sync with their respective Rust definitions +Used in the CI checks to ensure the currently tracked Go FFI bindings are in sync with their respective Rust definitions. ### `FOREST_DB_DEV_MODE`