lint: upgrade lint scripts for worktrees
What changed, and why it matters
This commit is a routine developer tooling change. It replaces one Python script that runs code-quality checks in a Docker container with another script that does the same thing but also works correctly with Git worktrees. There is no change to Bitcoin's network code, wallet code, consensus rules, or anything end users interact with. It does not fix or introduce a security vulnerability.
No security action needed. Treat as normal build/CI maintenance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors the CI lint execution path. The old .github/ci-lint-exec.py is replaced by ci/lint.py, and the GitHub Actions workflow is updated to create a temporary worktree and run the new script from it. The new script detects Git worktrees by reading the .git gitdir: pointer and mounts the real .git directory as a read-only Docker volume so git commands inside the container still work. The README is updated to document the simpler local invocation. No cryptographic, consensus, networking, or privilege-sensitive code is modified.
Changed components
ci/lint.py (new).github/ci-lint-exec.py (removed).github/workflows/ci.ymltest/lint/README.mdInspect captured patch +88 / −63
diff --git a/.github/ci-lint-exec.py b/.github/ci-lint-exec.py
deleted file mode 100755
index 73749acc..00000000
--- a/.github/ci-lint-exec.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or https://opensource.org/license/mit.
-
-import os
-import shlex
-import subprocess
-import sys
-import time
-
-
-def run(cmd, **kwargs):
- print("+ " + shlex.join(cmd), flush=True)
- kwargs.setdefault("check", True)
- try:
- return subprocess.run(cmd, **kwargs)
- except Exception as e:
- sys.exit(e)
-
-
-def main():
- CONTAINER_NAME = os.environ["CONTAINER_NAME"]
-
- build_cmd = [
- "docker", "buildx", "build",
- f"--tag={CONTAINER_NAME}",
- *shlex.split(os.getenv("DOCKER_BUILD_CACHE_ARG", "")),
- "--file=./ci/lint_imagefile",
- "."
- ]
-
- if run(build_cmd, check=False).returncode != 0:
- print("Retry building image tag after failure")
- time.sleep(3)
- run(build_cmd)
-
- extra_env = []
- if os.environ["GITHUB_EVENT_NAME"] == "pull_request":
- extra_env = ["--env", "LINT_CI_IS_PR=1"]
- if os.environ["GITHUB_EVENT_NAME"] != "pull_request" and os.environ["GITHUB_REPOSITORY"] == "bitcoin/bitcoin":
- extra_env = ["--env", "LINT_CI_SANITY_CHECK_COMMIT_SIG=1"]
-
- run([
- "docker",
- "run",
- "--rm",
- *extra_env,
- f"--volume={os.getcwd()}:/bitcoin",
- CONTAINER_NAME,
- ])
-
-
-if __name__ == "__main__":
- main()
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f54e0661..bbac5e02 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -681,4 +681,6 @@ jobs:
cache-provider: ${{ needs.runners.outputs.provider }}
- name: CI script
- run: python .github/ci-lint-exec.py
+ run: |
+ git worktree add ../lint-worktree HEAD
+ ../lint-worktree/ci/lint.py
diff --git a/ci/lint.py b/ci/lint.py
new file mode 100755
index 00000000..8e2a1e55
--- /dev/null
+++ b/ci/lint.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# Copyright (c) The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or https://opensource.org/license/mit/.
+
+import os
+import shlex
+import subprocess
+import sys
+import time
+from pathlib import Path
+
+
+def run(cmd, **kwargs):
+ print("+ " + shlex.join(cmd), flush=True)
+ kwargs.setdefault("check", True)
+ try:
+ return subprocess.run(cmd, **kwargs)
+ except Exception as e:
+ sys.exit(str(e))
+
+
+def get_worktree_mounts(repo_root):
+ git_path = repo_root / ".git"
+ if not git_path.is_file():
+ return []
+ content = git_path.read_text().strip()
+ if not content.startswith("gitdir: "):
+ return []
+ gitdir = (repo_root / content.removeprefix("gitdir: ")).resolve()
+ main_gitdir = gitdir.parent.parent
+ return [
+ f"--volume={gitdir}:{gitdir}",
+ f"--volume={main_gitdir}:{main_gitdir}:ro",
+ ]
+
+
+def main():
+ repo_root = Path(__file__).resolve().parent.parent
+ is_ci = os.environ.get("GITHUB_ACTIONS") == "true"
+ container = "bitcoin-linter"
+
+ build_cmd = [
+ "docker",
+ "buildx",
+ "build",
+ f"--tag={container}",
+ *shlex.split(os.environ.get("DOCKER_BUILD_CACHE_ARG", "")),
+ f"--file={repo_root}/ci/lint_imagefile",
+ str(repo_root),
+ ]
+ if run(build_cmd, check=False).returncode != 0:
+ if is_ci:
+ print("Retry building image after failure")
+ time.sleep(3)
+ run(build_cmd)
+
+ extra_env = []
+ if is_ci:
+ if os.environ.get("GITHUB_EVENT_NAME") == "pull_request":
+ extra_env = ["--env", "LINT_CI_IS_PR=1"]
+ elif os.environ.get("GITHUB_REPOSITORY") == "bitcoin/bitcoin":
+ extra_env = ["--env", "LINT_CI_SANITY_CHECK_COMMIT_SIG=1"]
+
+ run(
+ [
+ "docker",
+ "run",
+ "--rm",
+ *extra_env,
+ f"--volume={repo_root}:/bitcoin",
+ *get_worktree_mounts(repo_root),
+ *([] if is_ci else ["-it"]),
+ container,
+ *sys.argv[1:],
+ ]
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/README.md b/test/lint/README.md
index e28eda1f..462e4c80 100644
--- a/test/lint/README.md
+++ b/test/lint/README.md
@@ -3,20 +3,17 @@ This folder contains lint scripts.
Running locally
===============
-To run linters locally with the same versions as the CI environment, use the included
-Dockerfile:
+To run linters locally with the same versions as the CI environment use
+the _lint.py_ helper script which runs checks inside the CI container:
```sh
-DOCKER_BUILDKIT=1 docker build --platform=linux --tag=bitcoin-linter --file="./ci/lint_imagefile" ./ && docker run --rm -v $(pwd):/bitcoin -it bitcoin-linter
+./ci/lint.py
```
-Building the container can be done every time, because it is fast when the
-result is cached and it prevents issues when the image changes.
-
test runner
===========
-To run all the lint checks in the test runner outside the docker you first need
+To run all the lint checks in the test runner outside the container you first need
to install the rust toolchain using your package manager of choice or
[rustup](https://www.rust-lang.org/tools/install).
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.