From 32e6fbe05aab3bf624601d8f2750d4da4cd35358 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 15:10:40 +0200 Subject: [PATCH 01/13] Add libevm/README.md --- libevm/README.md | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 libevm/README.md diff --git a/libevm/README.md b/libevm/README.md new file mode 100644 index 000000000000..435a7a0f390b --- /dev/null +++ b/libevm/README.md @@ -0,0 +1,135 @@ +# libevm + +The Ethereum Virtual Machine (EVM) as a library, `libevm` is a fork of [`geth`](https://github.com/ethereum/go-ethereum) with injectable configuration directives. +Although designed to support the Avalanche [C-Chain](https://github.com/ava-labs/coreth) and [EVM-L1s](https://github.com/ava-labs/subnet-evm) (formerly *subnets*), configuration is general-purpose and backwards-compatible with `geth`. +We are immensely grateful for the hard work of the `geth` authors, and hope that our contribution can be of value to others too. Thank you! + +Libevm is notably following these rules: + +- minimal changes to geth-original code +- any extension to the code must be compatible with geth +- any extension to the code must be done in separate files, usually ending with `.libevm.go` or `.libevm_test.go` + +This allows for: + +- easy Geth fork-sync upgrade +- Common code base to use for its consumers [coreth](https://github.com/ava-labs/coreth) and [subnet-evm](https://github.com/ava-labs/subnet-evm), with customization defined on the consumer side. + +The reason for this fork was essentially to reduce the maintenance burden of [coreth](https://github.com/ava-labs/coreth) and [subnet-evm](https://github.com/ava-labs/subnet-evm): + +- upgrading the geth base was very complex, long and risky +- there was a constant worry about breaking changes in geth: is this geth source code here, will this make it incompatible with Geth? + +## Geth upgrade process + +1. libevm should be in sync with the targeted geth release, with a release tag ready to be used for its consumers. +1. Consumers (coreth and subnet-evm) should bump their libevm dependency version to the one previously created. +1. Consumers (coreth and subnet-evm) should manually compare and adapt their code to match the targeted geth release code. +This is how it was done before libevm, the difference being that now this is a reduced effort. + +## Implementation of libevm + +libevm relies on "hooks", which allows for libevm consumers to define a custom behavior for various places in the geth code, with minimal changes to the geth original code. + +Essentially a hook is an interface defined in libevm, and implemented in its consumers. + +Each hook is *registered* by the consumer by calling **once** at global scope a libevm-defined `RegisterExtras` function, with its own implementation of the hook. If the `RegisterExtras` function is not called, the hook defaults to a no-op hook, matching Geth's original behavior; this is the case in libevm code on its own. + +The following highlights some of the hooks defined in libevm, together with examples of their implementations in libevm consumers. These are based on: + +- libevm as of commit [979064c](https://github.com/ava-labs/libevm/blob/979064cfdbc1aa8dfae118dfb1344db34d37a164) +- coreth as of commit [0d68be6](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa) +- subnet-evm as of commit [4f496a0](https://github.com/ava-labs/subnet-evm/blob/4f496a00f226309aa701d33ac28b33658bb2b697) + + Note it may become outdated as the code evolves, but it can still serve as an example. + +### `params.ChainConfigHooks` + +The `ChainConfigHooks` interface can be implemented and registered to customize the behavior of the chain configuration in libevm. It is defined in [`params/hooks.libevm.go`](../params/hooks.libevm.go). + +The hook is retrieved from the method `ChainConfig.Hooks()`. + +It is used in: + +- the `ChainConfig.Description` method, to append the libevm consumer's description to the Geth chain configuration description, using the hook method `Description` method. +- the `ChainConfig.checkCompatible` method, to check if the libevm consumer's chain configuration is compatible on top of the Geth compatibility check, using the hook method `CheckConfigCompatible` method. +- the `ChainConfig.CheckConfigForkOrder` method, to check if the libevm consumer's chain configuration has its fork order correct on top of the Geth fork order check, using the hook method `CheckConfigForkOrder` method. + +Implementations of the hooks: + +- [coreth](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa/params/extras/config.go#L113) +- [subnet-evm](https://github.com/ava-labs/subnet-evm/blob/4f496a00f226309aa701d33ac28b33658bb2b697/params/extras/config.go#L118) + +### `params.RulesHooks` + +The `RulesHooks` interface can be implemented and registered to customize the behavior of the chain configuration rules in libevm. It is defined in [`params/hooks.libevm.go`](../params/hooks.libevm.go). + +The hook is retrieved from the method `Rules.Hooks()`. + +It is used in: + +- the `core/vm` package `EVM.canCreateContract` method, to check if, according to the libevm consumer rules, a contract can be created, using the hook method `CanCreateContract`. +- the `core/vm` package `EVM.precompile` method, to override precompile if needed, according to the libevm consumer rules, and using the hook method `PrecompileOverride`. +- the `core/vm` package `ActivePrecompiles` function, to add the libevm consumer active precompiles to the active precompiles of Geth, using the hook method `ActivePrecompiles`. +- the `core` package `StateTransition.TransitionDb` method, to check if the libevm consumer rules allow to execute a transaction, using the hook method `CanExecuteTransaction`. + +Implementations of the hooks: + +- [coreth](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa/params/hooks_libevm.go#L25) +- [subnet-evm](https://github.com/ava-labs/subnet-evm/blob/4f496a00f226309aa701d33ac28b33658bb2b697/params/hooks_libevm.go#L24) + +### `core/vm.Hooks` + +The `vm.Hooks` interface can be implemented and registered to customize the behavior of the EVM in libevm. It is defined in [`core/vm/hooks.libevm.go`](../core/vm/hooks.libevm.go). + +The hook is registered with `RegisterHooks` and is directly assigned to a global variable `libevmHooks`, which is then used to access the hook. + +It is used in: + +- the `core/vm` package `NewEVM` function, to override EVM creation arguments (such as the block context, state database, etc.), using the hook method `OverrideNewEVMArgs`. +- the `core/vm` package `EVM.Reset` method, to override EVM reset arguments (transaction context and state database), using the hook method `OverrideEVMResetArgs`. + +Implementations of the hooks: + +- [coreth](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa/core/evm.go#L50) +- [subnet-evm](https://github.com/ava-labs/subnet-evm/blob/4f496a00f226309aa701d33ac28b33658bb2b697/core/evm.go#L50) + +### `core/types.HeaderHooks` + +The `HeaderHooks` interface can be implemented and registered to customize the behavior of the block header in libevm. It is defined in [`core/types/block.libevm.go`](../core/types/block.libevm.go). + +The hook is retrieved from the method `Header.hooks()`. + +It is used exclusively in the `core/types` package in: + +- the `CopyHeader` function, to deep copy extra header fields defined by the libevm consumer, using the hook method `PostCopy`. +- the Header JSON encoding and decoding, by hooking its `EncodeJSON` and `DecodeJSON` methods respectively in the `Header.MarshalJSON` and `Header.UnmarshalJSON` methods. +- the Header RLP encoding and decoding, by hooking its `EncodeRLP` and `DecodeRLP` methods respectively in the `Header.EncodeRLP` and `Header.DecodeRLP` methods. + +Implementations of the hooks: + +- [coreth](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa/plugin/evm/customtypes/header_ext.go#L37) +- [subnet-evm](https://github.com/ava-labs/subnet-evm/blob/4f496a00f226309aa701d33ac28b33658bb2b697/plugin/evm/customtypes/header_ext.go#L37) + +### `core/types.BlockBody` + +The `BlockBody` interface can be implemented and registered to customize the behavior of the block **and** body in libevm. It is defined in [`core/types/block.libevm.go`](../core/types/block.libevm.go). + +The hook is retrieved either from: + +- the method `Block.hooks()` +- the method `Body.hooks()` + +This is because the hook, so far in the libevm consumers, has the same "payload" (fields in the implementation of `BlockBody`). It does have different methods for the `Block` and `Body` though, as explained below. + +It is used exclusively in the `core/types` package in: + +- the Block RLP encoding by injecting the entire hooks in the RLP encoding through `Block.EncodeRLP`. The hook method `BlockRLPFieldsForEncoding` is then picked up in the hook-compatible added code `extblock.EncodeRLP`, where `extblock` is the geth-internal typed used for RLP encoding. Its `EncodeRLP` method is added code to customize the RLP encoding through hooks. +- the Block RLP decoding by injecting the entire hooks in the RLP decoding through `Block.DecodeRLP`. The hook method `BlockRLPFieldPointersForDecoding` is then picked up in the hook-compatible added code `extblock.DecodeRLP`, where `extblock` is the geth-internal typed used for RLP decoding. Its `DecodeRLP` method is added code to customize the RLP decoding through hooks. +- the Body RLP encoding by using the hook method `BodyRLPFieldsForEncoding` directly in the `Body.EncodeRLP` method, which is new separate code. +- the Body RLP decoding by using the hook method `BodyRLPFieldPointersForDecoding` directly in the `Body.DecodeRLP` method, which is new separate code. + +Implementations of the hooks: + +- [coreth](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa/plugin/evm/customtypes/block_ext.go#L22) +- subnet-evm: **NONE!** Because subnet-evm uses the same header, block and body as Geth, there is no need for hooks or extra payloads. From 592901322eb388c38d6f0bd4b8ffe15ddc593ca5 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 15:14:35 +0200 Subject: [PATCH 02/13] Move libevm specific pieces in libevm/README.md and refer from README.md to libevm/README.md --- README.md | 14 +------------- libevm/README.md | 4 ++++ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0f18436b40f6..7344bca45838 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,6 @@ # libevm -[![API Reference]( -https://pkg.go.dev/badge/github.com/ava-labs/libevm -)](https://pkg.go.dev/github.com/ava-labs/libevm?tab=doc) -[![Go Report Card](https://goreportcard.com/badge/github.com/ava-labs/libevm)](https://goreportcard.com/report/github.com/ava-labs/libevm) -[![Go Build & Test](https://github.com/ava-labs/libevm/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/ava-labs/libevm/actions/workflows/go.yml) - -The Ethereum Virtual Machine (EVM) as a library, `libevm` is a fork of [`geth`](https://github.com/ethereum/go-ethereum) with -injectable configuration directives. Although designed to support the Avalanche [C-Chain](https://github.com/ava-labs/coreth) and -[EVM-L1s](https://github.com/ava-labs/subnet-evm) (formerly _subnets_), configuration is general-purpose and backwards-compatible -with `geth`. - - -We are immensely grateful for the hard work of the `geth` authors, and hope that our contribution can be of value to others too. Thank you! +Please refer to [`libevm/README.md](libevm/README.md) for the libevm specific readme. Below is geth original readme. ## Go Ethereum diff --git a/libevm/README.md b/libevm/README.md index 435a7a0f390b..60f708dd9d26 100644 --- a/libevm/README.md +++ b/libevm/README.md @@ -1,5 +1,9 @@ # libevm +[![API Reference](https://pkg.go.dev/badge/github.com/ava-labs/libevm)](https://pkg.go.dev/github.com/ava-labs/libevm?tab=doc) +[![Go Report Card](https://goreportcard.com/badge/github.com/ava-labs/libevm)](https://goreportcard.com/report/github.com/ava-labs/libevm) +[![Go Build & Test](https://github.com/ava-labs/libevm/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/ava-labs/libevm/actions/workflows/go.yml) + The Ethereum Virtual Machine (EVM) as a library, `libevm` is a fork of [`geth`](https://github.com/ethereum/go-ethereum) with injectable configuration directives. Although designed to support the Avalanche [C-Chain](https://github.com/ava-labs/coreth) and [EVM-L1s](https://github.com/ava-labs/subnet-evm) (formerly *subnets*), configuration is general-purpose and backwards-compatible with `geth`. We are immensely grateful for the hard work of the `geth` authors, and hope that our contribution can be of value to others too. Thank you! From bfd30e98834894e758eca52bac83da8a638982b8 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 15:16:09 +0200 Subject: [PATCH 03/13] Clarify common payload for block and body --- libevm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/README.md b/libevm/README.md index 60f708dd9d26..85779e7a9f65 100644 --- a/libevm/README.md +++ b/libevm/README.md @@ -124,7 +124,7 @@ The hook is retrieved either from: - the method `Block.hooks()` - the method `Body.hooks()` -This is because the hook, so far in the libevm consumers, has the same "payload" (fields in the implementation of `BlockBody`). It does have different methods for the `Block` and `Body` though, as explained below. +This is because the hook, so far in the libevm consumers, has the same "payload" (fields in the implementation of `BlockBody`) for both the Body and the Block. It does have different methods for the `Block` and `Body` though, as explained below. It is used exclusively in the `core/types` package in: From 05a00616ba5b2fc9fadc38e374c478c9e3e7c532 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 15:33:34 +0200 Subject: [PATCH 04/13] Add CI checks for markdown in the libevm directory --- .github/workflows/lint.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 67428073c803..7f6add5f88d7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -63,3 +63,18 @@ jobs: uses: ludeeus/action-shellcheck@2.0.0 with: scandir: './libevm' + + markdown: + runs-on: ubuntu-latest + steps: + - name: Lint + uses: DavidAnson/markdownlint-cli2-action@05f32210e84442804257b2a6f20b273450ec8265 # v19.1.0 + with: + globs: | + libevm/**.md + config: .markdownlint.json + - name: Link and misspelling check + uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 # v1.3.4 + with: + filter_mode: diff_context + fail_level: any From 1a473e039a696b851bd5740662e862da0fe7064c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 16:11:16 +0200 Subject: [PATCH 05/13] Fix: the header is not the same as geth for subnet-evm --- libevm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/README.md b/libevm/README.md index 85779e7a9f65..1e28f5f6147c 100644 --- a/libevm/README.md +++ b/libevm/README.md @@ -136,4 +136,4 @@ It is used exclusively in the `core/types` package in: Implementations of the hooks: - [coreth](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa/plugin/evm/customtypes/block_ext.go#L22) -- subnet-evm: **NONE!** Because subnet-evm uses the same header, block and body as Geth, there is no need for hooks or extra payloads. +- subnet-evm: **NONE!** Because subnet-evm uses the same block and body as Geth, there is no need for hooks or extra payloads. From a7eebd29af0363604a634055535f94b93cfe07ff Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 16:16:13 +0200 Subject: [PATCH 06/13] Add missing markdownlint config --- .github/workflows/configs/.markdownlint.json | 3 +++ .github/workflows/lint.yml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/configs/.markdownlint.json diff --git a/.github/workflows/configs/.markdownlint.json b/.github/workflows/configs/.markdownlint.json new file mode 100644 index 000000000000..734813196985 --- /dev/null +++ b/.github/workflows/configs/.markdownlint.json @@ -0,0 +1,3 @@ +{ + "MD013": false +} \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7f6add5f88d7..6dcdc7e54afb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -62,7 +62,7 @@ jobs: - name: Run ShellCheck uses: ludeeus/action-shellcheck@2.0.0 with: - scandir: './libevm' + scandir: "./libevm" markdown: runs-on: ubuntu-latest @@ -72,7 +72,7 @@ jobs: with: globs: | libevm/**.md - config: .markdownlint.json + config: .github/workflows/configs/.markdownlint.json - name: Link and misspelling check uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 # v1.3.4 with: From 601438c0b7aa23b975e7a1a9e31ef49a049b4905 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 16:18:56 +0200 Subject: [PATCH 07/13] Try fixing markdown lint CI --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6dcdc7e54afb..2110f647390d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -72,7 +72,7 @@ jobs: with: globs: | libevm/**.md - config: .github/workflows/configs/.markdownlint.json + config: ../.github/workflows/configs/.markdownlint.json - name: Link and misspelling check uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 # v1.3.4 with: From b1e6bdb07b2099e0f239187f50f86ce11afaf4fd Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 16:20:31 +0200 Subject: [PATCH 08/13] Try fixing markdown lint CI --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2110f647390d..8df93cc12020 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -72,7 +72,7 @@ jobs: with: globs: | libevm/**.md - config: ../.github/workflows/configs/.markdownlint.json + config: ../../.github/workflows/configs/.markdownlint.json - name: Link and misspelling check uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 # v1.3.4 with: From a48090a8bc18e35a9db301a870e734fe12323211 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 16:23:05 +0200 Subject: [PATCH 09/13] Where I am --- .github/workflows/lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8df93cc12020..a0f92b001763 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -67,6 +67,7 @@ jobs: markdown: runs-on: ubuntu-latest steps: + - run: pwd && ls -a - name: Lint uses: DavidAnson/markdownlint-cli2-action@05f32210e84442804257b2a6f20b273450ec8265 # v19.1.0 with: From 21169959a7bb3cc98d6f8854587d14da6a98e6e8 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 16:24:28 +0200 Subject: [PATCH 10/13] OK I had to checkout I guess --- .github/workflows/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a0f92b001763..8b6e73fb090c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -67,13 +67,13 @@ jobs: markdown: runs-on: ubuntu-latest steps: - - run: pwd && ls -a + - uses: actions/checkout@v4 - name: Lint uses: DavidAnson/markdownlint-cli2-action@05f32210e84442804257b2a6f20b273450ec8265 # v19.1.0 with: globs: | libevm/**.md - config: ../../.github/workflows/configs/.markdownlint.json + config: .github/workflows/configs/.markdownlint.json - name: Link and misspelling check uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 # v1.3.4 with: From f735930c0af4b809f3838d1d545fd8dc0fd312e6 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 3 Apr 2025 16:34:25 +0200 Subject: [PATCH 11/13] Clarify subnet-evm has a different body than geth --- libevm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/README.md b/libevm/README.md index 1e28f5f6147c..f71c7d6921d4 100644 --- a/libevm/README.md +++ b/libevm/README.md @@ -136,4 +136,4 @@ It is used exclusively in the `core/types` package in: Implementations of the hooks: - [coreth](https://github.com/ava-labs/coreth/blob/0d68be6b92be7c34095487b3a512b87b8b923caa/plugin/evm/customtypes/block_ext.go#L22) -- subnet-evm: **NONE!** Because subnet-evm uses the same block and body as Geth, there is no need for hooks or extra payloads. +- subnet-evm: **NONE!** Because subnet-evm uses the same block and body (body at least from an RLP point of view) as Geth, there is no need for hooks or extra payloads. From aa6f97fc72200a1505faaafd8b825b0354e90a78 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 10 Apr 2025 14:19:06 +0200 Subject: [PATCH 12/13] Re-add intro in root readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7344bca45838..430abf57085e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # libevm -Please refer to [`libevm/README.md](libevm/README.md) for the libevm specific readme. Below is geth original readme. +The Ethereum Virtual Machine (EVM) as a library, `libevm` is a fork of [`geth`](https://github.com/ethereum/go-ethereum) with injectable configuration directives. +Although designed to support the Avalanche [C-Chain](https://github.com/ava-labs/coreth) and [EVM-L1s](https://github.com/ava-labs/subnet-evm) (formerly *subnets*), configuration is general-purpose and backwards-compatible with `geth`. +We are immensely grateful for the hard work of the `geth` authors, and hope that our contribution can be of value to others too. Thank you! + +Please refer to [`libevm/README.md`](libevm/README.md) for the libevm specific readme. Below is geth original readme. ## Go Ethereum From 2727571015e80c800746dfa7dc6ae79ea45c98bf Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 10 Apr 2025 14:20:58 +0200 Subject: [PATCH 13/13] Add markdown job to lint needs array --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8b6e73fb090c..29ce58a77d1a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,7 +22,7 @@ jobs: # list of the `lint` job as this is what gates PRs. lint: runs-on: ubuntu-latest - needs: [golangci-lint, yamllint, shellcheck] + needs: [golangci-lint, yamllint, shellcheck, markdown] steps: - run: echo "Dependencies successful"