diff --git a/.claude/commands/edit-workflow.md b/.claude/commands/edit-workflow.md index f576b8572fb..9c906e5ebfa 100644 --- a/.claude/commands/edit-workflow.md +++ b/.claude/commands/edit-workflow.md @@ -15,4 +15,4 @@ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 ## Validation -Run `uvx tox -e static` before committing — this runs `actionlint` to validate YAML syntax and structure. +Run `just lint-actions` before committing to validate YAML syntax and structure. diff --git a/.claude/commands/lint.md b/.claude/commands/lint.md index 9d6f54ddac0..bc5f3b12f84 100644 --- a/.claude/commands/lint.md +++ b/.claude/commands/lint.md @@ -5,18 +5,17 @@ Run the full static analysis suite and fix issues. This matches the CI check on ## Step 1: Run the Full Check ```bash -uvx tox -e static +just static ``` This runs ruff, mypy, codespell, ethereum-spec-lint, and actionlint in one pass. If everything passes, you're done. ## Step 2: Auto-Fix Formatting and Lint Issues -If tox reports ruff errors, run these first — they resolve most issues automatically: +If static checks report ruff errors, run the fix recipe first — it resolves most issues automatically: ```bash -uv run ruff format -uv run ruff check --fix +just fix ``` ## Step 3: Resolve Remaining Issues Manually @@ -24,12 +23,12 @@ uv run ruff check --fix After auto-fix, re-run to see what's left: ```bash -uvx tox -e static +just static ``` - **Remaining ruff issues**: fix manually (auto-fix can't handle all rules) - **mypy errors**: fix type annotations, add missing types, correct signatures -- **codespell errors**: fix typos, or add intentional words to `whitelist.txt` +- **codespell errors**: fix typos, or add intentional words via `just whitelist ` - **ethereum-spec-lint errors**: fix import isolation violations (see `/implement-eip` for import rules) - **actionlint errors**: fix workflow YAML issues (see `/edit-workflow`) @@ -38,5 +37,5 @@ uvx tox -e static Re-run until clean: ```bash -uvx tox -e static +just static ``` diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0d5b8b038dc..d0e5e1a21b7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,9 +9,9 @@ N/A. ## ✅ Checklist -- [ ] All: Ran fast `tox` checks to avoid unnecessary CI fails, see also [Code Standards](https://eest.ethereum.org/main/getting_started/code_standards/) and [Enabling Pre-commit Checks](https://eest.ethereum.org/main/dev/precommit/): +- [ ] All: Ran fast static checks to avoid unnecessary CI fails, see also [Code Standards](https://eest.ethereum.org/main/getting_started/code_standards/) and [Enabling Pre-commit Checks](https://eest.ethereum.org/main/dev/precommit/): ```console - uvx tox -e static + just static ``` - [ ] All: PR title adheres to the [repo standard](https://eest.ethereum.org/main/getting_started/contributing/?h=contri#commit-messages-issue-and-pr-titles) - it will be used as the squash commit message and should start `type(scope):`. - [ ] All: Considered updating the online docs in the [./docs/](/ethereum/execution-specs/blob/HEAD/docs/) directory. diff --git a/.github/actions/merge-eip-branches/action.yaml b/.github/actions/merge-eip-branches/action.yaml index 63c1b13556e..1235303cf4e 100644 --- a/.github/actions/merge-eip-branches/action.yaml +++ b/.github/actions/merge-eip-branches/action.yaml @@ -114,7 +114,7 @@ runs: done echo "Running static checks on merged branch" - uvx --with=tox-uv tox -e static + just static echo "All EIP branches merged successfully" diff --git a/.github/actions/rebase-eip-branch/action.yaml b/.github/actions/rebase-eip-branch/action.yaml index 84a6dc9a78a..c3cf0afb0a4 100644 --- a/.github/actions/rebase-eip-branch/action.yaml +++ b/.github/actions/rebase-eip-branch/action.yaml @@ -85,7 +85,7 @@ runs: echo "Rebase of ${EIP_BRANCH} successful" echo "Running static checks on ${EIP_BRANCH}" - uvx --with=tox-uv tox -e static + just static done - name: Push rebased branches diff --git a/.github/actions/setup-uv/action.yaml b/.github/actions/setup-uv/action.yaml index 578d7fde3b0..cf251242a69 100644 --- a/.github/actions/setup-uv/action.yaml +++ b/.github/actions/setup-uv/action.yaml @@ -1,5 +1,5 @@ -name: Setup uv and tox -description: Install uv, Python, and tox for CI jobs +name: Setup uv and just +description: Install uv, Python, and just for CI jobs inputs: python-version: description: Python version to install (e.g. "3.14", "pypy3.11") @@ -14,7 +14,7 @@ runs: using: "composite" steps: - name: Install uv and python ${{ inputs.python-version }} - uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0 + uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0 with: enable-cache: ${{ inputs.enable-cache }} cache-dependency-glob: ${{ inputs.cache-dependency-glob }} @@ -23,6 +23,7 @@ runs: - name: Prefer uv-managed Python over system Python shell: bash run: echo "UV_PYTHON_PREFERENCE=only-managed" >> "$GITHUB_ENV" - - name: Install tox - shell: bash - run: uv tool install tox + - name: Install just + uses: taiki-e/install-action@e24b8b7a939c6a537188f34a4163cb153dd85cf6 # v2.69.1 + with: + tool: just@1.46 diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml index ca2720e7f20..165bfa52039 100644 --- a/.github/workflows/benchmark.yaml +++ b/.github/workflows/benchmark.yaml @@ -44,7 +44,7 @@ jobs: type: benchmark - name: Run benchmark unit tests - run: tox -e tests_benchmark_pytest_py3 + run: just test-tests-bench env: EVM_BIN: ${{ steps.evm-builder.outputs.evm-bin }} @@ -57,11 +57,11 @@ jobs: matrix: include: - name: Benchmark Gas Values - tox-env: benchmark-gas-values + recipe: bench-gas - name: Fixed Opcode Count CLI - tox-env: benchmark-fixed-opcode-cli + recipe: bench-opcode - name: Fixed Opcode Count Config - tox-env: benchmark-fixed-opcode-config + recipe: bench-opcode-config steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -77,7 +77,7 @@ jobs: type: benchmark - name: Run ${{ matrix.name }} - run: tox -e ${{ matrix.tox-env }} + run: just ${{ matrix.recipe }} env: EVM_BIN: ${{ steps.evm-builder.outputs.evm-bin }} diff --git a/.github/workflows/gh-pages.yaml b/.github/workflows/gh-pages.yaml index a9201b67344..eb5d3ae78bf 100644 --- a/.github/workflows/gh-pages.yaml +++ b/.github/workflows/gh-pages.yaml @@ -35,8 +35,8 @@ jobs: - name: Build Documentation run: | - tox -e spec-docs - touch .tox/docs/.nojekyll + just docs-spec + touch .just/docs-spec/.nojekyll env: DOCC_SKIP_DIFFS: ${{ case(github.event_name == 'push' && github.ref_name == github.event.repository.default_branch, '', '1') }} @@ -44,7 +44,7 @@ jobs: id: artifact uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 with: - path: .tox/docs + path: .just/docs-spec deploy: needs: build diff --git a/.github/workflows/test-checklist.yaml b/.github/workflows/test-checklist.yaml index c8ee2d0a94a..2134dfb944c 100644 --- a/.github/workflows/test-checklist.yaml +++ b/.github/workflows/test-checklist.yaml @@ -28,4 +28,4 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-uv - name: Run checklist consistency test - run: tox -e tests_pytest_py3 -- -k test_checklist_template_consistency + run: just test-tests -k test_checklist_template_consistency diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 237e8eb953b..7017135a890 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -30,8 +30,8 @@ jobs: - name: Checkout ethereum/execution-specs uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-uv - - name: Build html documentation with mkdocs via tox - run: tox -e mkdocs + - name: Build html documentation with mkdocs + run: just docs changelog: name: Validate changelog entries @@ -40,10 +40,10 @@ jobs: - name: Checkout ethereum/execution-specs uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-uv - - name: Run changelog validation via tox - run: tox -e changelog + - name: Run changelog validation + run: just changelog - markdownlint: + lint-md: name: Lint markdown files with markdownlint runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9a0ae1198f1..b328615180e 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,7 +12,7 @@ on: - ".gitignore" - ".vscode/**" - "whitelist.txt" - - "docs/**" + - "docs/**" - "mkdocs.yml" workflow_dispatch: pull_request: @@ -22,7 +22,7 @@ on: - ".gitignore" - ".vscode/**" - "whitelist.txt" - - "docs/**" + - "docs/**" - "mkdocs.yml" concurrency: @@ -47,7 +47,7 @@ jobs: shell: bash run: echo "version=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')" >> "$GITHUB_OUTPUT" - name: Restore mypy cache - uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: .mypy_cache key: mypy-${{ runner.os }}-py${{ steps.python.outputs.version }}-${{ hashFiles('uv.lock') }}-${{ github.sha }} @@ -55,10 +55,10 @@ jobs: mypy-${{ runner.os }}-py${{ steps.python.outputs.version }}-${{ hashFiles('uv.lock') }}- mypy-${{ runner.os }}-py${{ steps.python.outputs.version }}- - name: Run static checks - run: tox -e static + run: just static - name: Save mypy cache if: always() - uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: .mypy_cache key: mypy-${{ runner.os }}-py${{ steps.python.outputs.version }}-${{ hashFiles('uv.lock') }}-${{ github.sha }} @@ -73,8 +73,8 @@ jobs: EOF uvx --from actionlint-py actionlint - py3: - name: py3 (${{ matrix.label }}) + fill: + name: fill (${{ matrix.label }}) runs-on: [self-hosted-ghr, size-xl-x64] needs: static strategy: @@ -104,18 +104,18 @@ jobs: with: python-version: "3.14" - uses: ./.github/actions/setup-env - - name: Run py3 tests (${{ matrix.label }}) - run: tox -e py3 -- --from ${{ matrix.from_fork }} --until ${{ matrix.until_fork }} + - name: Run fill (${{ matrix.label }}) + run: just fill --from ${{ matrix.from_fork }} --until ${{ matrix.until_fork }} env: PYTEST_XDIST_AUTO_NUM_WORKERS: auto - name: Upload coverage reports to Codecov uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: - files: .tox/coverage.xml + files: .just/fill/coverage.xml flags: unittests token: ${{ secrets.CODECOV_TOKEN }} - pypy3: + fill-pypy: runs-on: [self-hosted-ghr, size-xl-x64] needs: static steps: @@ -126,13 +126,13 @@ jobs: with: python-version: "pypy3.11" - uses: ./.github/actions/setup-env - - name: Run pypy3 tests - run: tox -e pypy3 + - name: Run fill-pypy tests + run: just fill-pypy env: PYPY_GC_MAX: "2G" PYPY_GC_MIN: "1G" - json_loader: + json-loader: runs-on: [self-hosted-ghr, size-xl-x64] needs: static steps: @@ -141,12 +141,12 @@ jobs: submodules: recursive - uses: ./.github/actions/setup-uv - uses: ./.github/actions/setup-env - - name: Fill and run json_loader tests - run: tox -e json_loader + - name: Fill and run json-loader tests + run: just json-loader env: PYTEST_XDIST_AUTO_NUM_WORKERS: auto - tests_pytest_py3: + test-tests: runs-on: [self-hosted-ghr, size-xl-x64] needs: static steps: @@ -156,12 +156,12 @@ jobs: - uses: ./.github/actions/setup-uv - uses: ./.github/actions/setup-env - uses: ./.github/actions/build-evmone - - name: Run py3 tests - run: tox -e tests_pytest_py3 + - name: Run test-tests + run: just test-tests env: PYTEST_XDIST_AUTO_NUM_WORKERS: auto - tests_pytest_pypy3: + test-tests-pypy: runs-on: [self-hosted-ghr, size-xl-x64] needs: static steps: @@ -173,8 +173,8 @@ jobs: python-version: "pypy3.11" - uses: ./.github/actions/setup-env - uses: ./.github/actions/build-evmone - - name: Run pypy3 tests - run: tox -e tests_pytest_pypy3 + - name: Run test-tests-pypy + run: just test-tests-pypy env: PYPY_GC_MAX: "2G" PYPY_GC_MIN: "1G" diff --git a/.gitignore b/.gitignore index 827d04d6a69..3769738d037 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ pip-log.txt pip-delete-this-directory.txt .tox/ +.just/ /doc/_autosummary diff --git a/CLAUDE.md b/CLAUDE.md index 1e0d5b85923..243d6704a96 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Ethereum Execution Layer Specification written in Python. This is a **specificat ## Tooling -- **uv** is the package manager. **tox** orchestrates test environments (`uvx tox -al`). +- **uv** is the package manager. **just** is the command runner (`just --list`). - The `execution_testing` package under `packages/testing/` is a UV workspace member. ## Linting diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1db4ab1e6bc..0846469c60f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -150,6 +150,7 @@ This saves you having to apply code review feedback repeatedly for each fork. Running the tests necessary to merge into the repository requires: - [`uv`](https://docs.astral.sh/uv/) package manager, +- [`just`](https://just.systems/) command runner (install via your package manager, [`uv tool install just-bin`](https://pypi.org/project/just-bin/), or [pre-built binaries](https://just.systems/man/en/pre-built-binaries.html)), - Python 3.11.x, - [PyPy](https://www.pypy.org/) [7.3.19](https://downloads.python.org/pypy/) or later. - `geth` installed and present in `$PATH`. @@ -166,19 +167,26 @@ Or, if you've already cloned the repository, you can fetch the submodules with: git submodule update --init --recursive ``` -The tests can be run with: +The static checks can be run with: ```bash -tox +just static ``` -The development tools can also be run outside of `tox`, and can automatically reformat the code: +Individual checks and auto-fix are also available as recipes: ```bash -uv run ruff check # Detects code issues and produces a report to STDOUT. -uv run ruff check --fix # Fixes minor code issues (like unsorted imports). -uv run ruff format # Formats code. -uv run mypy # Verifies type annotations. +just fix # Auto-fix formatting and lint issues. +just lint # Lint with ruff. +just format-check # Check formatting with ruff. +just typecheck # Run type checking with mypy. +just spellcheck # Check spelling with codespell. +``` + +Run `just` to see all available recipes. Recipes that accept arguments can be passed extra flags, for example: + +```bash +just typecheck --warn-unreachable ``` > [!TIP] @@ -189,7 +197,29 @@ uv run mypy # Verifies type annotations. > export MYPY_CACHE_DIR=~/path/to/execution-specs/.mypy_cache > ``` -It is recommended to use a [virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to keep your system Python installation clean. +### Shell Auto-Completion + +`just` provides tab-completion for recipe names and arguments. To enable it for your shell: + +**Bash** — add to `~/.bashrc`: + +```bash +eval "$(just --completions bash)" +``` + +**Zsh** — add to `~/.zshrc`: + +```bash +eval "$(just --completions zsh)" +``` + +**Fish** — run once (fish auto-loads completions from this directory): + +```bash +just --completions fish > ~/.config/fish/completions/just.fish +``` + +After restarting your shell (or sourcing the config), `just ` will complete recipe names. A trace of the EVM execution for any test case can be obtained by providing the `--evm-trace` argument to pytest. Note: Make sure to run the EVM trace on a small number of tests at a time. The log might otherwise get very big. @@ -219,7 +249,7 @@ The command takes 4 arguments, 2 of which are optional As an example, if one wants to create baseline code for the `Spurious Dragon` fork from the `Tangerine Whistle` one ```bash -ethereum-spec-new-fork --from_fork="Tangerine Whistle" --to_fork="Spurious Dragon" --from_test=EIP150 --to_test=EIP158 +uv run ethereum-spec-new-fork --from_fork="Tangerine Whistle" --to_fork="Spurious Dragon" --from_test=EIP150 --to_test=EIP158 ``` The following will have to however, be updated manually @@ -266,7 +296,7 @@ The tool takes the following command line arguments As an example, if one wants to apply changes made in `Frontier` fork to `Homestead` and `Tangerine Whistle` ```bash -python src/ethereum_spec_tools/patch_tool.py frontier homestead tangerine_whistle +uv run python src/ethereum_spec_tools/patch_tool.py frontier homestead tangerine_whistle ``` ### Lint Tool @@ -279,4 +309,4 @@ The tool currently performs the following checks - The order of the identifiers between each hardfork is consistent. - Import statements follow the relevant import rules in modules. -The command to run the tool is `ethereum-spec-lint` +The command to run the tool is `just lint-spec` (or `uv run ethereum-spec-lint`). diff --git a/EIP_AUTHORS_MANUAL.md b/EIP_AUTHORS_MANUAL.md index 2d549e23cfb..f7ec268c7cd 100644 --- a/EIP_AUTHORS_MANUAL.md +++ b/EIP_AUTHORS_MANUAL.md @@ -54,7 +54,7 @@ Implementing a new EIP in the `execution-specs` repository involves the followin 1. **Create a new branch**: Create a new branch for the EIP under the appropriate fork. For example, if you are implementing an EIP for the Prague fork, create a new branch under `eips//eip-`. 2. **Implement the EIP**: Implement the EIP in the `src/ethereum/` folder. -3. **Basic sanity checks**: Run `tox -e static` to run basic formatting and linting checks. +3. **Basic sanity checks**: Run `just static` to run basic formatting and linting checks. 4. **Raise a PR**: Raise a PR against the appropriate branch. For example, if you are implementing an EIP for the Prague fork, raise a PR against the `forks/prague` branch. An EIP can only be CFI'd (Considered For Inclusion) if it has a reference `execution-specs` implementation. The EIP author is responsible for maintaining their EIP up-to-date with the latest changes. For example, if an author had written their EIP for Cancun under `eips/cancun/eip-x`, but for some reason it didn't make it into Cancun, they would need to rebase their EIP to reflect the changes in Prague under `eips/prague/eip-x`. diff --git a/Justfile b/Justfile new file mode 100644 index 00000000000..1567a516e73 --- /dev/null +++ b/Justfile @@ -0,0 +1,298 @@ +set positional-arguments := true + +alias help := list + +# List available recipes (default) +[default, private] +list: + @just --list + +root := justfile_directory() +output_dir := root / ".just" +xdist_workers := env("PYTEST_XDIST_AUTO_NUM_WORKERS", "6") +evm_bin := env("EVM_BIN", "evm") +latest_fork := "Amsterdam" + +# --- Static Analysis --- + +# Auto-fix formatting and lint issues +[group('static analysis')] +fix: + uv run ruff format + uv run ruff check --fix + +# Run all static checks (spellcheck, lint, format, mypy, ...) +[group('static analysis'), parallel] +static: typecheck lint-spec spellcheck lint-actions lock-check format-check lint + +# Check spelling +[group('static analysis')] +spellcheck: + #!/usr/bin/env bash + if ! uv run codespell; then + echo "" + echo "If false positive, add to whitelist:" + echo " just whitelist " + echo "" + echo "To auto-fix interactively:" + echo " uv run codespell -i 3" + exit 1 + else + echo "uv run codespell # passed!" + fi + +# Add a word to the spellcheck whitelist +[group('static analysis')] +whitelist *words: + uv run whitelist "$@" + +# Lint with ruff +[group('static analysis')] +lint *args: + uv run ruff check "$@" + +# Check formatting with ruff +[group('static analysis')] +format-check *args: + uv run ruff format --check "$@" + +# Run type checking with mypy +[group('static analysis')] +typecheck *args: + uv run mypy "$@" + +# Check EELS import isolation +[group('static analysis')] +lint-spec: + uv run ethereum-spec-lint + +# Verify uv.lock is up to date +[group('static analysis')] +lock-check: + #!/usr/bin/env bash + if ! uv lock --check; then + echo "" + echo "To sync the lock file:" + echo " uv lock" + echo "" + echo "Then commit the updated uv.lock." + exit 1 + fi + +# Lint GitHub Actions workflows +[group('static analysis')] +lint-actions: + uv run actionlint -pyflakes pyflakes -shellcheck "shellcheck -S warning" + +# Generate HTML coverage report from last just fill run +[group('consensus tests')] +coverage: + uv run coverage html -d "{{ output_dir }}/fill/coverage-html" + +# --- Fill Tests --- + +# Fill the consensus tests using EELS (with Python) +[group('consensus tests')] +fill *args: + @mkdir -p "{{ output_dir }}/fill/tmp" "{{ output_dir }}/fill/logs" + uv run fill \ + -m "not slow" \ + -n {{ xdist_workers }} --dist=loadgroup \ + --skip-index \ + --output="{{ output_dir }}/fill/fixtures" \ + --cov-config=pyproject.toml \ + --cov=ethereum \ + --cov-report=term \ + --cov-report "xml:{{ output_dir }}/fill/coverage.xml" \ + --no-cov-on-fail \ + --cov-branch \ + --basetemp="{{ output_dir }}/fill/tmp" \ + --log-to "{{ output_dir }}/fill/logs" \ + --clean \ + --until "{{ latest_fork }}" \ + --durations=50 \ + "$@" \ + tests + +# Fill the base coverage consensus tests using EELS with PyPy +[group('integration tests')] +fill-pypy *args: + @mkdir -p "{{ output_dir }}/fill-pypy/tmp" "{{ output_dir }}/fill-pypy/logs" + uv run --python pypy3.11 fill \ + --skip-index \ + --output="{{ output_dir }}/fill-pypy/fixtures" \ + --no-html \ + --tb=long \ + -ra \ + --show-capture=no \ + --disable-warnings \ + -m "eels_base_coverage and not derived_test" \ + -n auto --maxprocesses 7 \ + --dist=loadgroup \ + --basetemp="{{ output_dir }}/fill-pypy/tmp" \ + --log-to "{{ output_dir }}/fill-pypy/logs" \ + --clean \ + --until "{{ latest_fork }}" \ + --ignore=tests/ported_static \ + "$@" \ + tests + +# --- Integration Tests --- + +# Fill the base coverage consensus tests and run EELS against the fixtures +[group('integration tests')] +json-loader *args: + @mkdir -p "{{ output_dir }}/json-loader/tmp" + uv run fill \ + -m "eels_base_coverage and not derived_test" \ + --until "{{ latest_fork }}" \ + -n {{ xdist_workers }} --dist=loadgroup \ + --skip-index \ + --clean \ + --ignore=tests/ported_static \ + --output="{{ output_dir }}/json-loader/fixtures" \ + --cov-config=pyproject.toml \ + --cov=ethereum \ + --cov-fail-under=85 + uv run pytest \ + -m "not slow" \ + -n auto --maxprocesses 6 --dist=loadfile \ + --basetemp="{{ output_dir }}/json-loader/tmp" \ + "$@" \ + tests/json_loader \ + "{{ output_dir }}/json-loader/fixtures" + +# --- Unit Tests --- + +# Run the testing package unit tests (with Python) +[group('unit tests')] +test-tests *args: + @mkdir -p "{{ output_dir }}/test-tests/tmp" + cd packages/testing && uv run pytest \ + -n {{ xdist_workers }} \ + --basetemp="{{ output_dir }}/test-tests/tmp" \ + --ignore=src/execution_testing/cli/pytest_commands/plugins/filler/tests/test_benchmarking.py \ + "$@" \ + src + +# Run the testing package unit tests (with PyPy) +[group('unit tests')] +test-tests-pypy *args: + @mkdir -p "{{ output_dir }}/test-tests-pypy/tmp" + cd packages/testing && uv run --python pypy3.11 pytest \ + -n auto --maxprocesses 6 \ + --basetemp="{{ output_dir }}/test-tests-pypy/tmp" \ + --ignore=src/execution_testing/cli/pytest_commands/plugins/filler/tests/test_benchmarking.py \ + "$@" \ + src + +# Run benchmark framework unit tests (with Python) +[group('unit tests')] +[group('benchmark tests')] +test-tests-bench *args: + @mkdir -p "{{ output_dir }}/test-tests-bench/tmp" + uv run pytest \ + --basetemp="{{ output_dir }}/test-tests-bench/tmp" \ + "$@" \ + packages/testing/src/execution_testing/cli/pytest_commands/plugins/filler/tests/test_benchmarking.py + +# --- Benchmarks --- + +# Fill benchmark tests with --gas-benchmark-values +[group('benchmark tests')] +bench-gas *args: + @mkdir -p "{{ output_dir }}/bench-gas/tmp" "{{ output_dir }}/bench-gas/logs" + uv run fill \ + --evm-bin="{{ evm_bin }}" \ + --gas-benchmark-values 1 \ + --generate-pre-alloc-groups \ + --fork Osaka \ + -m "not slow" \ + -n auto --maxprocesses 10 --dist=loadgroup \ + --output="{{ output_dir }}/bench-gas/fixtures" \ + --basetemp="{{ output_dir }}/bench-gas/tmp" \ + --log-to "{{ output_dir }}/bench-gas/logs" \ + --clean \ + "$@" \ + tests/benchmark/compute + +# Fill benchmark tests with --fixed-opcode-count 1 +[group('benchmark tests')] +bench-opcode *args: + @mkdir -p "{{ output_dir }}/bench-opcode/tmp" "{{ output_dir }}/bench-opcode/logs" + uv run fill \ + --evm-bin="{{ evm_bin }}" \ + --fixed-opcode-count 1 \ + --fork Osaka \ + -m repricing \ + -n auto --maxprocesses 10 --dist=loadgroup \ + -k "not test_alt_bn128 and not test_bls12_381 and not test_modexp" \ + --output="{{ output_dir }}/bench-opcode/fixtures" \ + --basetemp="{{ output_dir }}/bench-opcode/tmp" \ + --log-to "{{ output_dir }}/bench-opcode/logs" \ + --clean \ + "$@" \ + tests/benchmark/compute + +# Run benchmark_parser, then fill benchmark tests using its config +[group('benchmark tests')] +bench-opcode-config *args: + @mkdir -p "{{ output_dir }}/bench-opcode-config/tmp" "{{ output_dir }}/bench-opcode-config/logs" + uv run benchmark_parser + uv run fill \ + --evm-bin="{{ evm_bin }}" \ + --fixed-opcode-count \ + --fork Osaka \ + -m repricing \ + -n auto --maxprocesses 10 --dist=loadgroup \ + -k "not test_alt_bn128 and not test_bls12_381 and not test_modexp" \ + --output="{{ output_dir }}/bench-opcode-config/fixtures" \ + --basetemp="{{ output_dir }}/bench-opcode-config/tmp" \ + --log-to "{{ output_dir }}/bench-opcode-config/logs" \ + --clean \ + "$@" \ + tests/benchmark/compute + +# --- Docs --- + +# Generate documentation for EELS using docc +[group('docs')] +docs-spec: + uv run docc --output "{{ output_dir }}/docs-spec" + uv run python -c 'import pathlib; print("documentation available under file://{0}".format(pathlib.Path(r"{{ output_dir }}") / "docs-spec" / "index.html"))' + +# Build HTML site documentation with mkdocs +[group('docs')] +docs: + GEN_TEST_DOC_VERSION="local" DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib" uv run mkdocs build --strict -d "{{ output_dir }}/docs/site" + +# Build HTML site documentation with mkdocs (skip test case reference) +[group('docs')] +docs-fast: + FAST_DOCS=True GEN_TEST_DOC_VERSION="local" DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib" uv run mkdocs build --strict -d "{{ output_dir }}/docs/site" + +# Validate docs/CHANGELOG.md entries +[group('docs')] +changelog: + uv run validate_changelog + +# Lint markdown files (markdownlint) +[group('docs')] +lint-md: + uv run markdownlintcli2_soft_fail + +[private] +crops: + @uvx pycowsay==0.0.0.2 "ethereum is good" + +# --- Housekeeping --- + +# Remove caches and build artifacts (.pytest_cache, .mypy_cache, __pycache__, ...) +[group('housekeeping')] +clean *args: + uv run eest clean "$@" + +# Remove caches, build artifacts, .just, and .venv +[group('housekeeping')] +clean-all *args: + uv run eest clean --all "$@" diff --git a/README.md b/README.md index 00f059464b2..0eaee1ebe26 100644 --- a/README.md +++ b/README.md @@ -79,14 +79,18 @@ The execution specification is a python implementation of Ethereum that prioriti The Ethereum specification is maintained as a Python library, for better integration with tooling and testing. -Requires Python 3.11+ +Requires: + +* Python 3.11+, +* [`uv`](https://docs.astral.sh/uv/) package manager, +* [`just`](https://just.systems/) command runner (install via your package manager, [`uv tool install just-bin`](https://pypi.org/project/just-bin/), or [pre-built binaries](https://just.systems/man/en/pre-built-binaries.html)). ### Building Specification Documentation -Building the spec documentation is most easily done through [`tox`](https://tox.readthedocs.io/en/latest/): +Building the spec documentation: ```bash -uvx --with=tox-uv tox -e spec-docs +just docs-spec ``` The path to the generated HTML will be printed to the console. diff --git a/docs/dev/docs.md b/docs/dev/docs.md index 8673217ae51..e4536838b6b 100644 --- a/docs/dev/docs.md +++ b/docs/dev/docs.md @@ -13,13 +13,16 @@ uv sync One time build in strict mode: ```console -uv run mkdocs build --strict +just docs ``` -Perform all docs related checks via `tox` in parallel: +Run all docs related checks: ```console -uvx tox -e spellcheck,markdownlint,mkdocs --parallel +just spellcheck +just lint-md +just docs +just changelog ``` ### Local Deployment and Test diff --git a/docs/dev/test_actions_locally.md b/docs/dev/test_actions_locally.md index 4653c9b9392..b1c07016988 100644 --- a/docs/dev/test_actions_locally.md +++ b/docs/dev/test_actions_locally.md @@ -44,22 +44,18 @@ will output something similar to: ```bash INFO[0000] Using docker host 'unix:///var/run/docker.sock', and daemon socket 'unix:///var/run/docker.sock' -Stage Job ID Job name Workflow name Workflow file Events -0 evmone-coverage-diff evmone-coverage-diff Evmone Coverage Report coverage.yaml pull_request -0 deploy deploy Deploy Docs Main docs_main.yaml push -0 deploy deploy Deploy Docs Tags docs_tags.yaml push -0 features features Build and Package Fixtures fixtures.yaml push,workflow_dispatch -0 feature-names feature-names Build and Package Fixtures for a feature fixtures_feature.yaml push,workflow_dispatch -0 lint Lint python sources with ruff Tox tox_verify.yaml push,pull_request,workflow_dispatch -0 typecheck Typecheck python sources with mypy Tox tox_verify.yaml push,pull_request,workflow_dispatch -0 spellcheck Spellcheck sources with pyspelling Tox tox_verify.yaml push,pull_request,workflow_dispatch -0 markdownlint Lint markdown files with markdownlint Tox tox_verify.yaml push,pull_request,workflow_dispatch -0 mkdocs Build html documentation with mkdocs Tox tox_verify.yaml push,pull_request,workflow_dispatch -0 pytest_framework Run unit tests, ${{ matrix.os }}, ${{ matrix.python }} Tox tox_verify.yaml push,pull_request,workflow_dispatch -0 tests_deployed Fill tests, deployed, ${{ matrix.os }}, ${{ matrix.python }} Tox tox_verify.yaml push,pull_request,workflow_dispatch -1 build build Build and Package Fixtures fixtures.yaml push,workflow_dispatch -1 build build Build and Package Fixtures for a feature fixtures_feature.yaml push,workflow_dispatch -2 release release Build and Package Fixtures fixtures.yaml push,workflow_dispatch +Stage Job ID Job name Workflow name Workflow file Events +0 evmone-coverage-diff evmone-coverage-diff Evmone Coverage Report coverage.yaml pull_request +0 deploy deploy Deploy Docs Main docs_main.yaml push +0 deploy deploy Deploy Docs Tags docs_tags.yaml push +0 features features Build and Package Fixtures fixtures.yaml push,workflow_dispatch +0 feature-names feature-names Build and Package Fixtures for a feature fixtures_feature.yaml push,workflow_dispatch +0 static Run static checks Test test.yaml push,pull_request,workflow_dispatch +0 py3 Fill tests (Python) Test test.yaml push,pull_request,workflow_dispatch +0 docs Build html documentation with mkdocs Test Docs test-docs.yaml push,pull_request,workflow_dispatch +1 build build Build and Package Fixtures fixtures.yaml push,workflow_dispatch +1 build build Build and Package Fixtures for a feature fixtures_feature.yaml push,workflow_dispatch +2 release release Build and Package Fixtures fixtures.yaml push,workflow_dispatch 2 release release Build and Package Fixtures for a feature fixtures_feature.yaml push,workflow_dispatch ``` @@ -77,7 +73,7 @@ DEFAULT_PYTHON_VERSION=3.12 and use the `--var-file` option to specify the file: ```bash -gh act --workflows .github/workflows/tox_verify.yaml -s GITHUB_TOKEN=$(gh auth token) --var-file=gh_vars.txt -j lint +gh act --workflows .github/workflows/test.yaml -s GITHUB_TOKEN=$(gh auth token) --var-file=gh_vars.txt -j static ``` ### Running Workflows that use a Matrix Strategy @@ -85,7 +81,7 @@ gh act --workflows .github/workflows/tox_verify.yaml -s GITHUB_TOKEN=$(gh auth t This is optional, recent versions will automatically detect the matrix strategy and run supported values. To run a specific matrix value, use the `--matrix` option: ```bash -gh act --workflows .github/workflows/tox_verify.yaml -s GITHUB_TOKEN=$(gh auth token) --matrix python:3.12 -j pytest_framework +gh act --workflows .github/workflows/test.yaml -s GITHUB_TOKEN=$(gh auth token) --matrix python:3.12 -j test-tests ``` ### Running Release Workflows diff --git a/docs/getting_started/code_standards.md b/docs/getting_started/code_standards.md index f52e99d7c43..a1265aee339 100644 --- a/docs/getting_started/code_standards.md +++ b/docs/getting_started/code_standards.md @@ -4,19 +4,12 @@ This document outlines the coding standards and practices used in the @ethereum/ ## Code and CI Requirements -Code pushed to @ethereum/execution-spec-tests must fulfill the following checks in [CI](https://github.com/ethereum/execution-spec-tests/actions/workflows/tox_verify.yaml): - -| Type | Tox Command | Explanation | -| ---------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| Lint & code formatting | `uvx tox -e lint` | Python lint, format and module import check via `ruff` | -| Typecheck | `uvx tox -e typecheck` | Objects that provide typehints pass type-checking via `mypy`. | -| Framework unit tests | `uvx tox -e pytest` | All framework unit tests must execute correctly. | -| EL Client test cases | `uvx tox -e tests-deployed` | All client test cases for deployed forks can be generated. | -| Benchmark EL Test cases | `uvx tox -e tests-deployed-benchmark` | All client test cases specific to benchmarks for deployed forks can be generated. | -| HTML doc build | `uvx tox -e mkdocs` | Documentation generated without warnings. | -| Spellcheck | `uvx tox -e spellcheck` | Code and documentation spell-check using codespell. | -| Markdown lint | `uvx tox -e markdownlint` | Markdown lint (requires [additional dependency](code_standards_details.md#additional-dependencies)). | -| Changelog validation | `uvx tox -e changelog` | Validates changelog entries format and structure in `docs/CHANGELOG.md`. | +Code pushed to @ethereum/execution-spec-tests must pass the CI checks. Run `just` to see all available recipes, grouped by category. The most common checks: + +```console +just static # Run all static checks (lint, format, mypy, spellcheck, ...) +just fix # Auto-fix formatting and lint issues +``` !!! important "Avoid CI surprises - Use pre-commit hooks!" **We strongly encourage all contributors to install and use pre-commit hooks!** This will run fast checks (lint, typecheck, spellcheck) automatically before each commit, helping you catch issues early and avoid frustrating CI failures after pushing your changes. @@ -25,41 +18,15 @@ Code pushed to @ethereum/execution-spec-tests must fulfill the following checks ```console uvx pre-commit install ``` - - This saves you time by catching formatting issues, type errors, and spelling mistakes before they reach CI. - -!!! tip "Running checks easily" - - Add an alias: - - ```console - alias tox="uvx tox" - ``` - - Run all checks in parallel: - ```console - uvx tox run-parallel - ``` - - Run sequentially: - - ```console - uvx tox - ``` - - Run specific, faster checks: - - ```console - uvx tox -e lint,typecheck - ``` + This saves you time by catching formatting issues, type errors, and spelling mistakes before they reach CI. !!! tip "Lint & code formatting: Using `ruff` and VS Code to help autoformat and fix module imports" On the command-line, solve fixable issues with: ```console - uv run ruff check --fix + just fix ``` Use VS Code, see [VS Code Setup](../getting_started/setup_vs_code.md), to autoformat code, automatically organize Python module imports and highlight typechecking and spelling issues. @@ -96,8 +63,7 @@ A correctly configured editor will automatically handle most formatting requirem See the [Detailed Code Standards](code_standards_details.md) page for more information on: -- [Running tox environments](code_standards_details.md#running-tox-environments). - - Additional required [dependencies for markdownlint and spellchecking](code_standards_details.md#additional-dependencies). +- Additional required [dependencies for markdownlint](code_standards_details.md#additional-dependencies). - [Pre-commit hooks setup](code_standards_details.md#pre-commit-hooks). - [Verifying test fixture changes](code_standards_details.md#verifying-fixture-changes). - [Ignoring bulk change commits](code_standards_details.md#ignoring-bulk-change-commits) in `git blame`. diff --git a/docs/getting_started/code_standards_details.md b/docs/getting_started/code_standards_details.md index 44d1918416a..98739b18326 100644 --- a/docs/getting_started/code_standards_details.md +++ b/docs/getting_started/code_standards_details.md @@ -2,57 +2,22 @@ This page provides in-depth information about the code standards and verification processes in @ethereum/execution-spec-tests. -## Running Tox Environments +## Running Checks -### Execution Options - -Run all `tox` environments in parallel: - -```console -uvx tox run-parallel -``` - -Run environments sequentially with verbose output: +Run all static checks: ```console -uvx tox -v +just static ``` -List all available environments: +Run `just` to list all available recipes. Individual checks can be run directly, for example: ```console -uvx tox -av +just lint +just typecheck +just spellcheck ``` -### Specific Environment Commands - -Run specific environments using the `-e` flag: - -```console -uvx tox -e lint,typecheck,spellcheck -``` - -#### For Test Case Changes (`./tests/`) - -```console -uvx tox -e lint,typecheck,spellcheck,tests-deployed -``` - -#### For Framework and Library Changes (`./src/`) - -```console -uvx tox -e lint,typecheck,spellcheck,pytest -``` - -#### For Documentation Changes (`./docs/`) - -```console -uvx tox -e spellcheck,markdownlint,mkdocs,changelog -``` - -!!! note "Tox Virtual Environment" -Checks performed by `tox` are sandboxed in their own virtual environments (created automatically in the `.tox/` subdirectory). These can be used to debug errors encountered during `tox` execution. - ### Additional Dependencies Some checks require external (non-Python) packages: @@ -64,7 +29,7 @@ The spellcheck environment uses **codespell**, which is automatically installed To fix spelling errors found by codespell: ```console -uv run codespell *.md *.ini .github/ src/ tests/ docs/ --write-changes +uv run codespell --write-changes ``` !!! note "VS Code Integration" @@ -81,7 +46,7 @@ Or use a specific node version using `nvm`. ## Pre-commit Hooks -Certain `tox` environments can be run automatically as git pre-commit hooks to ensure that your changes meet the project's standards before committing. +Certain checks can be run automatically as git pre-commit hooks to ensure that your changes meet the project's standards before committing. ### Installation @@ -105,23 +70,17 @@ Both methods return a `RunResult` with `.ret`, `.outlines`, `.errlines`, `assert ## Building and Verifying Docs Locally -To quickly build and browse the HTML documentation locally run: - -=== "bash" +Build the full HTML documentation: - ```console - export FAST_DOCS=True - uv run mkdocs serve - ``` - -=== "fish" +```console +just docs +``` - ```console - set -x FAST_DOCS True - uv run mkdocs serve - ``` +For faster iteration (skips the "[Test Case Reference](https://eest.ethereum.org/main/tests/)" section): -Setting `FAST_DOCS` to `False` additionally builds the "[Test Case Reference](https://eest.ethereum.org/main/tests/)" Section. +```console +just docs-fast +``` ## Verifying Fixture Changes @@ -164,7 +123,7 @@ The `hasher compare` subcommand directly compares two fixture directories and shows only the differences: ```console -hasher compare fixtures/ fixtures_new/ +uv run hasher compare fixtures/ fixtures_new/ ``` | Flag | Description | diff --git a/docs/getting_started/installation_troubleshooting.md b/docs/getting_started/installation_troubleshooting.md index d5b701ffa01..032761ee3bd 100644 --- a/docs/getting_started/installation_troubleshooting.md +++ b/docs/getting_started/installation_troubleshooting.md @@ -120,7 +120,7 @@ This page provides guidance on how to troubleshoot common issues that may arise ## Problem: VS Code "Autoformat on Save" with Ruff Not Working !!! danger "Problem: 'Autoformat on Save' with Ruff not working as expected in VS Code" - If you are using VS Code and "autoformat on save" is not working as expected, or if it produces different formatting than the official `tox -e lint` command, you may have a version mismatch with the `ruff` formatter. This problem can be confirmed if `git diff` shows changes to an otherwise unmodified file after you have saved it. + If you are using VS Code and "autoformat on save" is not working as expected, or if it produces different formatting than `just lint`, you may have a version mismatch with the `ruff` formatter. This problem can be confirmed if `git diff` shows changes to an otherwise unmodified file after you have saved it. This issue often occurs when VS Code is not configured to use the project's virtual environment (`.venv`) or if the linting dependencies have not been installed. In this case, VS Code's Ruff extension falls back to a bundled version of `ruff`, which may not match the version pinned in the project's `pyproject.toml` file. diff --git a/docs/writing_tests/test_markers.md b/docs/writing_tests/test_markers.md index 97d89326982..90130cdf9f7 100644 --- a/docs/writing_tests/test_markers.md +++ b/docs/writing_tests/test_markers.md @@ -328,7 +328,7 @@ In this example, the test will be marked as expected to fail when it is being ex ### `@pytest.mark.slow` -This marker is used to mark tests that are slow to run. These tests are not run during [`tox` checks](./verifying_changes.md), and are only run when a release is being prepared. +This marker is used to mark tests that are slow to run. These tests are not run during [CI checks](./verifying_changes.md), and are only run when a release is being prepared. ### `@pytest.mark.pre_alloc_mutable` diff --git a/packages/testing/pyproject.toml b/packages/testing/pyproject.toml index f9b5960a7ab..bde2463e157 100644 --- a/packages/testing/pyproject.toml +++ b/packages/testing/pyproject.toml @@ -88,8 +88,7 @@ checklist = "execution_testing.cli.pytest_commands.checklist:checklist" generate_checklist_stubs = "execution_testing.cli.generate_checklist_stubs:generate_checklist_stubs" genindex = "execution_testing.cli.gen_index:generate_fixtures_index_cli" gentest = "execution_testing.cli.gentest:generate" -pyspelling_soft_fail = "execution_testing.cli.tox_helpers:pyspelling" -markdownlintcli2_soft_fail = "execution_testing.cli.tox_helpers:markdownlint" +markdownlintcli2_soft_fail = "execution_testing.cli.ci_helpers:markdownlint" order_fixtures = "execution_testing.cli.order_fixtures:order_fixtures" evm_bytes = "execution_testing.cli.evm_bytes:evm_bytes" hasher = "execution_testing.cli.hasher:main" @@ -99,7 +98,7 @@ groupstats = "execution_testing.cli.show_pre_alloc_group_stats:main" extract_config = "execution_testing.cli.extract_config:extract_config" compare_fixtures = "execution_testing.cli.compare_fixtures:main" modify_static_test_gas_limits = "execution_testing.cli.modify_static_test_gas_limits:main" -validate_changelog = "execution_testing.cli.tox_helpers:validate_changelog" +validate_changelog = "execution_testing.cli.ci_helpers:validate_changelog" benchmark_parser = "execution_testing.cli.benchmark_parser:main" [tool.setuptools.packages.find] diff --git a/packages/testing/src/execution_testing/cli/ci_helpers.py b/packages/testing/src/execution_testing/cli/ci_helpers.py new file mode 100644 index 00000000000..e24c569166e --- /dev/null +++ b/packages/testing/src/execution_testing/cli/ci_helpers.py @@ -0,0 +1,148 @@ +""" +CLI helper commands for CI static checks. + +Contains wrappers to markdownlint-cli2 and changelog validation that fail +silently if external tools are not available, to avoid disruption to +external contributors. +""" + +import re +import shutil +import subprocess +import sys +from pathlib import Path + +import click +import semver + + +def find_project_root() -> Path: + """Locate the root directory of this project.""" + # Search upwards from file location + script_dir = Path(__file__).resolve().parent + for parent in [script_dir, *script_dir.parents]: + if (parent / "pyproject.toml").exists() and (parent / ".git").exists(): + return parent + + raise FileNotFoundError( + "Unable to locate project root! " + "Looking for a directory with both pyproject.toml and .git." + ) + + +@click.command( + context_settings={ + "ignore_unknown_options": True, + "allow_extra_args": True, + } +) +@click.argument("args", nargs=-1, type=click.UNPROCESSED) +def markdownlint(args: tuple[str, ...]) -> None: + """ + Lint the markdown in ./README.md and ./docs/ using the external command + markdownlint-cli2. + + Silently fail if markdownlint-cli2 is not installed. + + Allows argument forwarding to markdownlint-cli2. + """ + expected_version = "0.20.0" + markdownlint = shutil.which("markdownlint-cli2") + if not markdownlint: + # Note: There's an additional step in test.yaml to run markdownlint- + # cli2 in GitHub Actions + click.echo( + "********* Install 'markdownlint-cli2' to enable markdown linting" + " *********\n" + "```\n" + f"sudo npm install -g markdownlint-cli2@{expected_version}\n" + "```" + ) + sys.exit(0) + + result = subprocess.run( + [markdownlint, "--version"], + capture_output=True, + text=True, + ) + if result.returncode == 0: + version_match = re.search(r"v?(\d+\.\d+\.\d+)", result.stdout) + installed_version = version_match.group(1) if version_match else None + if installed_version: + installed = semver.Version.parse(installed_version) + expected = semver.Version.parse(expected_version) + minor_mismatch = (installed.major, installed.minor) != ( + expected.major, + expected.minor, + ) + else: + minor_mismatch = False + if minor_mismatch: + lines = [ + f"WARNING: markdownlint-cli2 {installed_version} " + f"installed, CI uses {expected_version}", + "", + "Lint results may differ from CI.", + f" npm install -g markdownlint-cli2@{expected_version}", + ] + width = max(len(line) for line in lines) + 4 + border = "*" * width + box = "\n".join(f"* {line:<{width - 4}} *" for line in lines) + click.echo(f"\n{border}\n{box}\n{border}\n") + + args_list: list[str] = ( + list(args) if len(args) > 0 else ["./docs/**/*.md", "./*.md"] + ) + + command = ["node", markdownlint] + args_list + sys.exit(subprocess.run(command).returncode) + + +@click.command() +def validate_changelog() -> None: + """ + Validate changelog formatting to ensure bullet points end with proper + punctuation. + + Check that all bullet points (including nested ones) end with either: + - A period (.) for regular entries + - A colon (:) for section headers that introduce lists + """ + project_root = find_project_root() + changelog_path = Path(project_root / "docs/CHANGELOG.md") + + if not changelog_path.exists(): + click.echo(f"❌ Changelog file not found: {changelog_path}") + sys.exit(1) + + try: + with open(changelog_path, "r", encoding="utf-8") as f: + content = f.read() + except Exception as e: + click.echo(f"❌ Error reading changelog: {e}.") + sys.exit(1) + + # Find bullet points that don't end with period or colon + invalid_lines = [] + for line_num, line in enumerate(content.splitlines(), 1): + if re.match(r"^\s*-\s+", line) and re.search( + r"[^\.:]$", line.rstrip() + ): + invalid_lines.append((line_num, line.strip())) + + if invalid_lines: + click.echo( + f"❌ Found bullet points in {changelog_path} without proper " + "punctuation:" + ) + click.echo() + for line_num, line in invalid_lines: + click.echo(f"Line {line_num}: {line}") + click.echo() + click.echo("💡 All bullet points should end with:") + click.echo(" - A period (.) for regular entries.") + click.echo(" - A colon (:) for paragraphs that introduce lists.") + sys.exit(1) + else: + click.echo("✅ All bullet points have proper punctuation!") + sys.exit(0) diff --git a/packages/testing/src/execution_testing/cli/eest/commands/clean.py b/packages/testing/src/execution_testing/cli/eest/commands/clean.py index 58bb90f9538..b2e1ba94fe2 100644 --- a/packages/testing/src/execution_testing/cli/eest/commands/clean.py +++ b/packages/testing/src/execution_testing/cli/eest/commands/clean.py @@ -12,7 +12,7 @@ "--all", "all_files", is_flag=True, - help="Remove the virtual environment and .tox directory as well.", + help="Remove the virtual environment and .just directories as well.", ) @click.option( "--dry-run", @@ -24,18 +24,18 @@ def clean(all_files: bool, dry_run: bool, verbose: bool) -> None: """ Remove all generated files and directories from the repository. - If `--all` is specified, the virtual environment and .tox directory will - also be removed. + If `--all` is specified, the virtual environment and .just directory + will also be removed. Args: - all_files (bool): Remove the virtual environment and .tox directory + all_files (bool): Remove the virtual environment and .just directory as well. dry_run (bool): Simulate the cleanup without removing files. verbose (bool): Show verbose output. - Note: The virtual environment and .tox directory are not removed by + Note: The virtual environment and .just directory are not removed by default. Example: Cleaning all generated files and directories and show the deleted @@ -46,7 +46,7 @@ def clean(all_files: bool, dry_run: bool, verbose: bool) -> None: Output: \b - 🗑️ Deleted: .tox + 🗑️ Deleted: .just 🗑️ Deleted: .venv 🗑️ Deleted: src/cli/et/__pycache__ 🗑️ Deleted: src/cli/et/commands/__pycache__ @@ -75,7 +75,7 @@ def clean(all_files: bool, dry_run: bool, verbose: bool) -> None: items_to_remove.extend(matching_files) if all_files: - items_to_remove.extend([".tox", ".venv"]) + items_to_remove.extend([".just", ".venv"]) # Perform dry run or actual deletion for item in items_to_remove: diff --git a/packages/testing/src/execution_testing/cli/tox_helpers.py b/packages/testing/src/execution_testing/cli/tox_helpers.py deleted file mode 100644 index cf14af6ee38..00000000000 --- a/packages/testing/src/execution_testing/cli/tox_helpers.py +++ /dev/null @@ -1,302 +0,0 @@ -""" -CLI commands used by tox.ini. - -Contains wrappers to the external commands markdownlint-cli2 and pyspelling -(requires aspell) that fail silently if the command is not available. The aim -is to avoid disruption to external contributors. -""" - -import os -import re -import shutil -import subprocess -import sys -from pathlib import Path - -import click -import semver -from pyspelling import __main__ as pyspelling_main # type: ignore -from rich.console import Console - - -def write_github_summary( - title: str, tox_env: str, error_message: str, fix_commands: list[str] -) -> None: - """ - Write a summary to GitHub Actions when a check fails. - - Args: - title: The title of the check that failed tox_env: The tox - environment name (e.g., "spellcheck") - tox_env: The tox environment - error_message: Description of what went wrong - fix_commands: List of commands to fix the issue locally - - """ - if not os.environ.get("GITHUB_ACTIONS"): - return - - summary_file = os.environ.get("GITHUB_STEP_SUMMARY") - if not summary_file: - return - - with open(summary_file, "a") as f: - f.write(f"## ❌ {title}\n\n") - f.write(f"{error_message}\n\n") - f.write("### To reproduce this check locally:\n") - f.write("```bash\n") - f.write(f"uvx tox -e {tox_env}\n") - f.write("```\n\n") - - if fix_commands: - f.write("### To verify and fix the issues:\n") - f.write("```bash\n") - for cmd in fix_commands: - f.write(f"{cmd}\n") - f.write("```\n") - - -def find_project_root() -> Path: - """Locate the root directory of this project.""" - # Search upwards from file location - script_dir = Path(__file__).resolve().parent - for parent in [script_dir, *script_dir.parents]: - if (parent / "pyproject.toml").exists() and (parent / ".git").exists(): - return parent - - raise FileNotFoundError( - "Unable to locate project root! " - "Looking for a directory with both pyproject.toml and .git." - ) - - -@click.command( - context_settings={ - "ignore_unknown_options": True, - "allow_extra_args": True, - } -) -@click.argument("args", nargs=-1, type=click.UNPROCESSED) -def markdownlint(args: tuple[str, ...]) -> None: - """ - Lint the markdown in ./README.md and ./docs/ using the external command - markdownlint-cli2. - - Silently fail if markdownlint-cli2 is not installed. - - Allows argument forwarding to markdownlint-cli2. - """ - expected_version = "0.20.0" - markdownlint = shutil.which("markdownlint-cli2") - if not markdownlint: - # Note: There's an additional step in test.yaml to run markdownlint- - # cli2 in GitHub Actions - click.echo( - "********* Install 'markdownlint-cli2' to enable markdown linting" - " *********\n" - "```\n" - f"sudo npm install -g markdownlint-cli2@{expected_version}\n" - "```" - ) - sys.exit(0) - - result = subprocess.run( - [markdownlint, "--version"], - capture_output=True, - text=True, - ) - if result.returncode == 0: - version_match = re.search(r"v?(\d+\.\d+\.\d+)", result.stdout) - installed_version = version_match.group(1) if version_match else None - if installed_version: - installed = semver.Version.parse(installed_version) - expected = semver.Version.parse(expected_version) - minor_mismatch = (installed.major, installed.minor) != ( - expected.major, - expected.minor, - ) - else: - minor_mismatch = False - if minor_mismatch: - lines = [ - f"WARNING: markdownlint-cli2 {installed_version} " - f"installed, CI uses {expected_version}", - "", - "Lint results may differ from CI.", - f" npm install -g markdownlint-cli2@{expected_version}", - ] - width = max(len(line) for line in lines) + 4 - border = "*" * width - box = "\n".join(f"* {line:<{width - 4}} *" for line in lines) - click.echo(f"\n{border}\n{box}\n{border}\n") - - args_list: list[str] = ( - list(args) if len(args) > 0 else ["./docs/**/*.md", "./*.md"] - ) - - command = ["node", markdownlint] + args_list - sys.exit(subprocess.run(command).returncode) - - -@click.command() -def pyspelling() -> None: - """ - Spellcheck the markdown in ./README.md and ./docs/ using the pyspelling - package. - - Silently fails if aspell is not installed (required by pyspelling). - - Command-line arguments are not forwarded to pyspelling. - """ - if not shutil.which("aspell"): - click.echo("aspell not installed, skipping spellcheck.") - if os.environ.get("GITHUB_ACTIONS"): - write_github_summary( - title="Pyspelling Check Failed", - tox_env="spellcheck", - error_message=( - "aspell is not installed. This tool is required for " - "spell checking documentation." - ), - fix_commands=[ - "# Install aspell on Ubuntu/Debian", - "sudo apt-get install aspell aspell-en", - "", - "# Install aspell on macOS", - "brew install aspell", - ], - ) - sys.exit(1) - else: - click.echo( - "********* Install 'aspell' and 'aspell-en' to enable " - "spellcheck *********" - ) - sys.exit(0) - - result = pyspelling_main.main() - if result != 0: - write_github_summary( - title="Pyspelling Check Failed", - tox_env="spellcheck", - error_message=( - "Pyspelling found spelling errors in the documentation." - ), - fix_commands=[ - "# Check the pyspelling configuration", - "cat .pyspelling.yml", - "", - "# Review and fix spelling errors manually", - "# Pyspelling doesn't have an auto-fix option", - ], - ) - sys.exit(result) - - -@click.command() -def codespell() -> None: - """ - Run codespell on the codebase and provide helpful error messages. - - Checks spelling in .github/, src/, tests/, and docs/ directories. - """ - console = Console() - - # Define the paths to check - paths_to_check = ["*.md", "*.ini", ".github/", "src/", "tests/", "docs/"] - paths_str = " ".join(paths_to_check) - - # Run codespell - result = subprocess.run( - ["codespell"] + paths_to_check, - capture_output=True, - text=True, - ) - - # Print the output - if result.stdout: - console.print(result.stdout) - if result.stderr: - console.print(result.stderr, style="red") - - # If there were spelling errors, show a helpful message - if result.returncode != 0: - console.print("\n[bold red]❌ Spellcheck Failed[/bold red]") - console.print( - "[yellow]Please review the errors above. For single-suggestion " - "fixes, you can automatically apply them with:[/yellow]" - ) - console.print( - f"[cyan]uv run codespell {paths_str} --write-changes[/cyan]\n" - ) - - # Write to GitHub Actions summary - write_github_summary( - title="Spellcheck Failed", - tox_env="spellcheck", - error_message="Codespell found spelling errors in the code.", - fix_commands=[ - "# Ensure codespell is installed (part of docs extras)", - "uv sync", - "", - "# Check for spelling errors", - f"uv run codespell {paths_str}", - "", - "# Automatically fix single-suggestion errors", - f"uv run codespell {paths_str} --write-changes", - ], - ) - - sys.exit(1) - - sys.exit(0) - - -@click.command() -def validate_changelog() -> None: - """ - Validate changelog formatting to ensure bullet points end with proper - punctuation. - - Checks that all bullet points (including nested ones) end with either: - - A period (.) for regular entries - - A colon (:) for section headers that introduce lists - """ - project_root = find_project_root() - changelog_path = Path(project_root / "docs/CHANGELOG.md") - - if not changelog_path.exists(): - click.echo(f"❌ Changelog file not found: {changelog_path}") - sys.exit(1) - - try: - with open(changelog_path, "r", encoding="utf-8") as f: - content = f.read() - except Exception as e: - click.echo(f"❌ Error reading changelog: {e}.") - sys.exit(1) - - # Find bullet points that don't end with period or colon - invalid_lines = [] - for line_num, line in enumerate(content.splitlines(), 1): - if re.match(r"^\s*-\s+", line) and re.search( - r"[^\.:]$", line.rstrip() - ): - invalid_lines.append((line_num, line.strip())) - - if invalid_lines: - click.echo( - f"❌ Found bullet points in {changelog_path} without proper " - "punctuation:" - ) - click.echo() - for line_num, line in invalid_lines: - click.echo(f"Line {line_num}: {line}") - click.echo() - click.echo("💡 All bullet points should end with:") - click.echo(" - A period (.) for regular entries.") - click.echo(" - A colon (:) for paragraphs that introduce lists.") - sys.exit(1) - else: - click.echo("✅ All bullet points have proper punctuation!") - sys.exit(0) diff --git a/packages/testing/src/execution_testing/client_clis/tests/test_transition_tools_support.py b/packages/testing/src/execution_testing/client_clis/tests/test_transition_tools_support.py index 73f8cf2d4d3..1d4a9e6fb14 100644 --- a/packages/testing/src/execution_testing/client_clis/tests/test_transition_tools_support.py +++ b/packages/testing/src/execution_testing/client_clis/tests/test_transition_tools_support.py @@ -48,6 +48,7 @@ fork_set.add(Prague) +@pytest.mark.xfail(reason="Known issue: geth evm not built in CI, see #2557") def test_ci_multi_t8n_support( installed_transition_tool_instances: Dict[str, TransitionTool | Exception], running_in_ci: bool, diff --git a/pyproject.toml b/pyproject.toml index dbe612b13a0..5433e780cdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -358,7 +358,7 @@ files = [ ] [tool.docc.output] -path = "api_docs" +path = ".just/docs-spec" extension = ".html" [tool.vulture] @@ -436,7 +436,7 @@ builtin = "clear,code,usage" skip = [ ".devcontainer", ".git", - ".tox", + ".just", ".venv", ".ruff_cache", ".mypy_cache", @@ -484,7 +484,7 @@ exclude = [ "^\\.git/", "^\\.pytest_cache/", "^\\.ruff_cache/", - "^\\.tox/", + "^\\.just/", "^\\.venv/", "^\\.vscode/", "^fixtures/", diff --git a/tox.ini b/tox.ini index 38f0259b61a..98b485cbbd5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,246 +1,85 @@ +# Migration stub: execution-specs now uses just (https://just.systems) +# Install just: https://just.systems/man/en/pre-built-binaries.html +# +# Run `just` to see all available recipes. + [tox] -requires = - tox>=4,<5 - tox-uv>=1.29,<2 -# Get a description of all available environments with `uvx tox -av` -envlist = - py3 - pypy3 - json_loader - static - tests_pytest_py3 - tests_pytest_pypy3 - tests_benchmark_pytest_py3 - benchmark-gas-values - benchmark-fixed-opcode-cli - benchmark-fixed-opcode-config - spec-docs - mkdocs - changelog - markdownlint +envlist = py3 [testenv] -runner = uv-venv-lock-runner -uv_seed = false -wheel_build_env = .pkg +allowlist_externals = printf, false +skip_install = true +deps = +commands = + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n Run \033[1m"just"\033[0m to see all available recipes.\n\n' + false -[testenv:static] -description = Run spelling, lint, typechecking and dependency checks +[testenv:py3] commands = - codespell - ruff check - ruff format --check - mypy - ethereum-spec-lint - uv lock --check - actionlint -pyflakes pyflakes -shellcheck "shellcheck -S warning" + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust fill\033[0m\n\n' + false -[testenv:tests_pytest_py3] -description = Run the testing package unit tests (with Python) -dependency_groups = - test - lint # TODO: add ruff for gentest; remove as part of ethereum/execution-specs#1715 -changedir = {toxinidir}/packages/testing -package = editable +[testenv:pypy3] commands = - pytest \ - -n {env:PYTEST_XDIST_AUTO_NUM_WORKERS:6} \ - --basetemp="{temp_dir}/pytest" \ - --ignore=src/execution_testing/cli/pytest_commands/plugins/filler/tests/test_benchmarking.py \ - {posargs} \ - src + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust fill-pypy\033[0m\n\n' + false -[testenv:tests_pytest_pypy3] -description = Run the testing package unit tests (with PyPy) -# overwrite default groups (dev) to avoid installing optimized in tox envs using pypy -dependency_groups = - test - lint # TODO: add ruff for gentest; remove as part of ethereum/execution-specs#1715 -passenv = - PYPY_GC_MAX - PYPY_GC_MIN -# run inside the package directory so pytest picks up packages/testing/pyproject.toml -changedir = {toxinidir}/packages/testing -package = editable +[testenv:json_loader] commands = - pytest \ - -n auto --maxprocesses 6 \ - --basetemp="{temp_dir}/pytest" \ - --ignore=src/execution_testing/cli/pytest_commands/plugins/filler/tests/test_benchmarking.py \ - {posargs} \ - src + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust json-loader\033[0m\n\n' + false -[testenv:tests_benchmark_pytest_py3] -description = Run benchmark framework unit tests (with Python) -# Running in serial as not many tests +[testenv:static] commands = - pytest \ - --basetemp="{temp_dir}/pytest" \ - {posargs} \ - packages/testing/src/execution_testing/cli/pytest_commands/plugins/filler/tests/test_benchmarking.py + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust static\033[0m\n\n' + false -[testenv:json_loader] -description = Fill and run the spec against test fixtures -dependency_groups = - test +[testenv:tests_pytest_py3] commands = - fill \ - -m "eels_base_coverage and not derived_test" \ - --until Amsterdam \ - -n {env:PYTEST_XDIST_AUTO_NUM_WORKERS:6} --dist=loadgroup \ - --skip-index \ - --clean \ - --ignore=tests/ported_static \ - --output="tests/json_loader/fixtures" \ - --cov-config=pyproject.toml \ - --cov=ethereum \ - # Coverage gate: if this drops, regenerate the minimal test set with - # minimize-tests (https://github.com/SamWilsn/minimize-tests). - --cov-fail-under=85 - pytest \ - -m "not slow" \ - -n auto --maxprocesses 6 --dist=loadfile \ - --basetemp="{temp_dir}/pytest" \ - {posargs} \ - tests/json_loader + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust test-tests\033[0m\n\n' + false -[testenv:py3] -description = Fill the tests using EELS (with Python) -dependency_groups = - test +[testenv:tests_pytest_pypy3] commands = - fill \ - -m "not slow" \ - # loadgroup: serialize xdist_group("bigmem") tests onto one worker to limit peak memory - -n {env:PYTEST_XDIST_AUTO_NUM_WORKERS:6} --dist=loadgroup \ - --skip-index \ - --cov-config=pyproject.toml \ - --cov=ethereum \ - --cov-report=term \ - --cov-report "xml:{toxworkdir}/coverage.xml" \ - --no-cov-on-fail \ - --cov-branch \ - --basetemp="{temp_dir}/pytest" \ - --log-to "{toxworkdir}/logs" \ - --clean \ - --until Amsterdam \ - --durations=50 \ - {posargs} \ - tests + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust test-tests-pypy\033[0m\n\n' + false -[testenv:pypy3] -description = Fill the tests using EELS (with PyPy) -# see comment for tests_pytest_pypy3 -dependency_groups = - test -passenv = - PYPY_GC_MAX - PYPY_GC_MIN +[testenv:tests_benchmark_pytest_py3] commands = - fill \ - --skip-index \ - --no-html \ - --tb=long \ - -ra \ - --show-capture=no \ - --disable-warnings \ - -m "eels_base_coverage and not derived_test" \ - -n auto --maxprocesses 7 \ - # loadgroup: serialize xdist_group("bigmem") tests onto one worker to limit peak memory - --dist=loadgroup \ - --basetemp="{temp_dir}/pytest" \ - --log-to "{toxworkdir}/logs" \ - --clean \ - --until Amsterdam \ - --ignore=tests/ported_static \ - {posargs} \ - tests + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust test-tests-bench\033[0m\n\n' + false [testenv:benchmark-gas-values] -description = Run benchmark tests with `--gas-benchmark-values` -dependency_groups = - test -passenv = - EVM_BIN commands = - fill \ - --evm-bin={env:EVM_BIN:evm} \ - --gas-benchmark-values 1 \ - --generate-pre-alloc-groups \ - --fork Osaka \ - -m "not slow" \ - -n auto --maxprocesses 10 --dist=loadgroup \ - --basetemp="{temp_dir}/pytest" \ - --log-to "{toxworkdir}/logs" \ - --clean \ - {posargs} \ - tests/benchmark/compute + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust bench-gas\033[0m\n\n' + false [testenv:benchmark-fixed-opcode-cli] -description = Run benchmark tests with `--fixed-opcode-count 1` -dependency_groups = - test -passenv = - EVM_BIN commands = - fill \ - --evm-bin={env:EVM_BIN:evm} \ - --fixed-opcode-count 1 \ - --fork Osaka \ - -m repricing \ - -n auto --maxprocesses 10 --dist=loadgroup \ - # TODO: remove skip once working for all precompiles PR#1955 - -k "not test_alt_bn128 and not test_bls12_381 and not test_modexp" \ - --basetemp="{temp_dir}/pytest" \ - --log-to "{toxworkdir}/logs" \ - --clean \ - {posargs} \ - tests/benchmark/compute + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust bench-opcode\033[0m\n\n' + false [testenv:benchmark-fixed-opcode-config] -description = Run `benchmark_parser` and the benchmark tests using the generated file -dependency_groups = - test -passenv = - EVM_BIN commands = - benchmark_parser - fill \ - --evm-bin={env:EVM_BIN:evm} \ - --fixed-opcode-count \ - --fork Osaka \ - -m repricing \ - -n auto --maxprocesses 10 --dist=loadgroup \ - # TODO: remove skip once working for all precompiles PR#1955 - -k "not test_alt_bn128 and not test_bls12_381 and not test_modexp" \ - --basetemp="{temp_dir}/pytest" \ - --log-to "{toxworkdir}/logs" \ - --clean \ - {posargs} \ - tests/benchmark/compute + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust bench-opcode-config\033[0m\n\n' + false [testenv:spec-docs] -description = Generate documentation for the specification code using docc -basepython = python3 -passenv = DOCC_SKIP_DIFFS commands = - docc --output "{toxworkdir}/docs" - python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs" / "index.html"))' + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust docs-spec\033[0m\n\n' + false [testenv:mkdocs] -description = Build HTML site documentation with mkdocs -setenv = - GEN_TEST_DOC_VERSION = "tox" - # Set DYLD_FALLBACK_LIBRARY_PATH: Required for `cairosvg` so tox can find `libcairo-2`? - # https://squidfunk.github.io/mkdocs-material/plugins/requirements/image-processing/?h=cairo#cairo-library-was-not-found - DYLD_FALLBACK_LIBRARY_PATH = /opt/homebrew/lib commands = - mkdocs build --strict + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust docs\033[0m\n\n' + false [testenv:changelog] -description = Validate docs/CHANGELOG.md entries -commands = validate_changelog +commands = + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust changelog\033[0m\n\n' + false [testenv:markdownlint] -description = Lint markdown files (markdownlint) -commands = markdownlintcli2_soft_fail +commands = + printf '\n\033[1m execution-specs has migrated from tox to just.\033[0m\n Install just: https://just.systems/man/en/pre-built-binaries.html\n\n This tox env can be run using just via:\n \033[1mjust lint-md\033[0m\n\n' + false