Skip to content

Conversation

Elvis339
Copy link
Contributor

@Elvis339 Elvis339 commented Sep 25, 2025

Why this should be merged

Enables composable CI by allowing repositories to setup AvalancheGo with custom dependency versions. This solves the problem where teams need AvalancheGo context for testing but can't easily compose different repo versions in their CI workflows.

More context

git clone https://github.com/ava-labs/firewood "${FIREWOOD_CLONE_DIR}" \

How this works

Prepares build environment with dependency replacements for custom workflows

How this was tested

Test Run: https://github.com/ava-labs/avalanchego/actions/runs/18103163342/job/51511123516

Workflow:

firewood-12-reexecution:

This test validates the setup action by configuring Firewood v0.0.12 and v0.0.13, then running the c-chain-reexecution benchmark. The setup phase works correctly - the benchmark failure is unrelated and outside this PR's scope.

Note: The setup script depends on a custom branch in Firewood.

Need to be documented in RELEASES.md?

No. Internal CI change

@Elvis339 Elvis339 changed the title Composable ci action ci: composable avalanchego action Sep 25, 2025
@Elvis339 Elvis339 self-assigned this Sep 25, 2025
@Elvis339 Elvis339 marked this pull request as ready for review September 26, 2025 10:43
@Copilot Copilot AI review requested due to automatic review settings September 26, 2025 10:43
Copy link
Contributor

@maru-ava maru-ava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this not implemented as a task that could be executed both locally and in CI? A task has the advantage of being easier to implement and maintain - since iteration doesn't require cycling through CI - and then it would be possible to trivially reproduce a binary without CI.

@Elvis339
Copy link
Contributor Author

Why was this not implemented as a task that could be executed both locally and in CI?

The Action approach was chosen for cross-repository consumption. But now that you mention Task I guess it could be made to to run locally and in CI and be exposed as GH action for cross-repository consumption.

@Elvis339
Copy link
Contributor Author

Switched to task that can execute both locally and in CI.

  • Extended build.sh with -f option signaling Firewood version
  • Added new script setup_firewood.sh
  • Refactored CI to only support setting Avalanchego without building targets

Copy link
Contributor

@maru-ava maru-ava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that coreth is the integration point for firewood, and coreth is only compatible with specific versions of avalanchego, I'm not sure this open-ended approach (supporting independent configuration of the versions of firewood, coreth and avalanchego) makes sense.

I'm also not sure why avalanchego should be the integration point. Coreth already runs avalanchego e2e tests for its pinned version of avalanchego. So maybe that same mechanism can be extended to support a coreth job for each of the targeted versions of firewood?

runs:
using: 'composite'
steps:
- name: Checkout AvalancheGo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only non-task step that I would expect to see in an action with the remain steps being part of a task.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. I can see the appeal of consolidating all go.mod updates in one place.

I'm thinking of keeping coreth/libevm updates explicit in the action makes the dependency changes visible and optional. They're simple go get commands, so putting them in setup_firewood.sh would add context/coupling without clear benefit - the script would need to handle optional arguments for dependencies it doesn't own.

That said, I'm not strongly opposed to consolidating them. Do you see a clear advantage to moving them into the script? Or is this more about having a consistent pattern for where dependency management happens?

Happy to restructure if there's a compelling reason I'm missing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern for the majority of our CI jobs is that they are trivial to reproduce locally. That means, apart from CI-specific setup like cloning, caching and installing non-nix deps, the meat of a given job is encapsulated in a task that can be invoked locally. CI jobs are then just the glue between Github Actions and our tasks. This ensures both reproducibility and simplifies development and maintenance of our jobs.

@Elvis339
Copy link
Contributor Author

Elvis339 commented Oct 1, 2025

Given that coreth is the integration point for firewood, and coreth is only compatible with specific versions of avalanchego, I'm not sure this open-ended approach (supporting independent configuration of the versions of firewood, coreth and avalanchego) makes sense.

I'm also not sure why avalanchego should be the integration point. Coreth already runs avalanchego e2e tests for its pinned version of avalanchego. So maybe that same mechanism can be extended to support a coreth job for each of the targeted versions of firewood?

Thanks for the feedback. Let me clarify why I chose avalanchego as the integration point:

The c-reexecution-benchmark tool lives in avalanchego and needs to test firewood
directly. If we configure this through coreth:

  1. We'd update firewood version in coreth
  2. Then update coreth in avalanchego
  3. Then run the benchmark in avalanchego

This adds an extra dependency hop when the benchmark tool is already in avalanchego
and could consume firewood directly.

As you mentioned, e2e tests exist in coreth but they're run from the context of avalanchego. My idea is that each of these libs (firewood, coreth) provides a way to build and setup themselves, and then avalanchego (or others) becomes the "lego builder" - composing these components as needed rather than going through the
dependency chain.

Does that reasoning make sense, or am I missing something about how coreth's e2e
infrastructure could handle this use case?

edit by maru-ava: sorry, I accidentally edited this when attempting to reply.

echo "Setting up Firewood FFI version: ${FIREWOOD_VERSION}" >&2
echo "Using workspace: ${WORKSPACE_PATH}" >&2

git clone https://github.com/ava-labs/firewood "${FIREWOOD_CLONE_DIR}" \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it desirable to clone just to get the file rather than using a raw url? And then the build-firewood.sh script will go off and do a full clone anyway.

I'm not sure why this would be at all preferable to just building with nix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. We can leverage building with nix once it's merged.


# Build or fetch Firewood FFI with custom workspace
# Capture only the last line which is the FFI path
FFI_PATH=$("${SETUP_FIREWOOD_SCRIPT}" "${FIREWOOD_VERSION}" --workspace "${WORKSPACE_PATH}" | tail -n 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not appear that the target script accepts a version. afaik only way to build a specific version would be to clone at said version. Is there an updated script that hasn't yet merged to firewood main?

go mod tidy
go mod download

# Output FFI path to stdout for consumption by other scripts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you intending that the script call itself? I don't see any other case of tailing the output to get the path. Maybe just assume $PWD/firewood/ffi?

build-with-firewood:
desc: Builds avalanchego with Firewood FFI (specify version with FIREWOOD_VERSION)
vars:
FIREWOOD_VERSION: '{{.FIREWOOD_VERSION | default "ffi/v0.0.12"}}'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make it clear that what is being accepted here is any of branch|tag|SHA?

fi

echo "Updating go.mod with FFI path: ${FFI_PATH}" >&2
go mod edit -replace github.com/ava-labs/firewood-go-ethhash/ffi="${FFI_PATH}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not ideal that after I execute this script, my working tree is now dirty and I have to make sure I don't accidentally commit the change.

avalanchego:
description: 'AvalancheGo version (commit SHA, branch name, or tag)'
required: false
default: ${{ github.sha }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reference only makes sense if the custom action will only be used in the avalanchego repo.

description: 'Firewood version (commit SHA, branch, tag, or ffi/vX.Y.Z for pre-built)'
required: false
default: ''
firewood-path:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under what circumstances will this actually be used?

shell: bash
run: |
if [ -n "${{ inputs.firewood-path }}" ]; then
"${{ inputs.checkout-path }}/scripts/setup_firewood.sh" "${{ inputs.firewood }}" "${{ inputs.firewood-path }}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Counter to my insistence on a task being the preferred entrypoint, the only way this custom action is going to be able to be used in other repos isif they have this same script in the same location, or the script is in the action dir and referred to with a relative path. Which doesn't preclude the use of a task, said task would just need to point to the updated script path.


| Input | Description | Required | Default |
|-------|-------------|----------|-----------------------|
| `checkout-path` | Directory path where AvalancheGo will be checked out | No | `'.'` |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this suggested?

|-------|-------------|----------|-----------------------|
| `checkout-path` | Directory path where AvalancheGo will be checked out | No | `'.'` |
| `avalanchego` | AvalancheGo version (commit SHA, branch, tag) | No | `'${{ github.sha }}'` |
| `firewood` | Firewood version. Consumer should run Firewood shared workflow first | No | `''` |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's the firewood dir?

@maru-ava
Copy link
Contributor

maru-ava commented Oct 2, 2025

This adds an extra dependency hop when the benchmark tool is already in avalanchego
and could consume firewood directly.

It doesn't add a dependency hop that isn't already there. coreth depends directly on both firewood and avalanchego. coreth and avalanchego are always mutually pinned in their master branches. Updating the pinning is a pain (ask any coreth maintainer), and ideally firewood benchmarking would be performed independently of changes to the pinning to minimize the pain.

iirc @aaronbuchwald has suggested moving the re-execution testing to the coreth repo? That could help minimize the work involved in testing firewood, since then the most recent version of the testing would always be available to run against a compatible version of firewood. Otherwise, being able to apply an updated test to a new version of firewood could require updating the pinning between avalanchego and coreth and that can be painful when unrelated breaking changes have been made on the avalanchego side.

The potential for the master branch of avalanchego containing breaking changes for coreth is why I'm not convinced of the wisdom of being able to configure SHAs of all 3 repos. I would expect instead to see avalanchego capable of testing against whatever version of coreth it's pinned to and a compatible version of firewood, coreth being able to test with whatever version of avalanchego it's pinned to and a compatible version of firewood, and firewood being able to test with a compatible version of coreth and its pinned version of avalanchego.

As you mentioned, e2e tests exist in coreth but they're run from the context of avalanchego. My idea is that each of these libs (firewood, coreth) provides a way to build and setup themselves, and then avalanchego (or others) becomes the "lego builder" - composing these components as needed rather than going through the
dependency chain.

Does that reasoning make sense, or am I missing something about how coreth's e2e
infrastructure could handle this use case?

I think your reasoning only makes sense if one ignores the potential for avalanchego introducing changes that break coreth (between updates to mutual pinning). I've been frustrated enough times trying to make cross-repo CI changes that I think that potential is worth considering.

The e2e job of which you speak validates that a change to coreth is compatible with the currently pinned version of avalanchego. An update to the version of firewood could be such a change. The machinery involved could even be reused to enable firewood testing e.g. build ffi, clone coreth, go mod replace for firewood ffi, run coreth script to clone the appropriate version of avalanchego and go mod replace for coreth, and then run the desired test from the avalanchego repo.

@Elvis339
Copy link
Contributor Author

Elvis339 commented Oct 5, 2025

This adds an extra dependency hop when the benchmark tool is already in avalanchego
and could consume firewood directly.

It doesn't add a dependency hop that isn't already there. coreth depends directly on both firewood and avalanchego. coreth and avalanchego are always mutually pinned in their master branches. Updating the pinning is a pain (ask any coreth maintainer), and ideally firewood benchmarking would be performed independently of changes to the pinning to minimize the pain.

iirc @aaronbuchwald has suggested moving the re-execution testing to the coreth repo? That could help minimize the work involved in testing firewood, since then the most recent version of the testing would always be available to run against a compatible version of firewood. Otherwise, being able to apply an updated test to a new version of firewood could require updating the pinning between avalanchego and coreth and that can be painful when unrelated breaking changes have been made on the avalanchego side.

The potential for the master branch of avalanchego containing breaking changes for coreth is why I'm not convinced of the wisdom of being able to configure SHAs of all 3 repos. I would expect instead to see avalanchego capable of testing against whatever version of coreth it's pinned to and a compatible version of firewood, coreth being able to test with whatever version of avalanchego it's pinned to and a compatible version of firewood, and firewood being able to test with a compatible version of coreth and its pinned version of avalanchego.

As you mentioned, e2e tests exist in coreth but they're run from the context of avalanchego. My idea is that each of these libs (firewood, coreth) provides a way to build and setup themselves, and then avalanchego (or others) becomes the "lego builder" - composing these components as needed rather than going through the
dependency chain.

Does that reasoning make sense, or am I missing something about how coreth's e2e
infrastructure could handle this use case?

I think your reasoning only makes sense if one ignores the potential for avalanchego introducing changes that break coreth (between updates to mutual pinning). I've been frustrated enough times trying to make cross-repo CI changes that I think that potential is worth considering.

The e2e job of which you speak validates that a change to coreth is compatible with the currently pinned version of avalanchego. An update to the version of firewood could be such a change. The machinery involved could even be reused to enable firewood testing e.g. build ffi, clone coreth, go mod replace for firewood ffi, run coreth script to clone the appropriate version of avalanchego and go mod replace for coreth, and then run the desired test from the avalanchego repo.

Your argument is that testing firewood variations from coreth's context provides better isolation from the avalanchego ↔ coreth synchronization pain. Since updating the pinning between avalanchego and coreth is frequently painful (especially when unrelated breaking changes exist), testing firewood from coreth means we only need to deal with firewood version changes while relying on coreth's already-validated avalanchego pin. This minimizes friction because we're varying one dimension (firewood) instead of potentially coordinating compatibility across all three repos.

Is that an accurate representation of your concern? Correct me if I'm wrong.


I think your approach is valid, and I want to explain why I'm proposing avalanchego as the integration point despite the challenges you've raised.

The current reality is that both load-test and c-chain-reexecution benchmarks live in avalanchego. Avalanchego has organically become the testing context where realistic integration testing happens - not by design, but because it's where dependency resolution naturally converges.

My goal is to enable teams (particularly the Firewood team) to easily run these existing tests without writing code or navigating the dependency chain. For example, from Firewood's CI (pseudocode):

- uses: ava-labs/setup-avalanchego@v1
  with:
    firewood-version: v0.0.13

- uses: ava-labs/load-test@v1
  with:
    duration: 10m

The vision is composability: each component (firewood, coreth, avalanchego) provides standardized setup/build scripts, and any repo can compose versions as needed. Since the test tools already exist in avalanchego, this approach allows teams to reuse them without duplicating infrastructure or coordination overhead.

To properly address your concerns, we'd likely need a broader plan to move these tests to more appropriate homes (perhaps coreth for firewood integration testing, or dedicated test harnesses). But that's a larger refactoring effort.

Does this help clarify my reasoning? I'm open to discussing whether the composability benefits outweigh the compatibility risks you've identified, or whether we should prioritize moving the tests first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

5 participants