Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or https://opensource.org/license/mit/.6import ipaddress7from pathlib import Path8import os9import shlex10import subprocess11import sys12import time15def run(cmd, **kwargs):16print("+ " + shlex.join(cmd), flush=True)17kwargs.setdefault("check", True)18try:19return subprocess.run(cmd, **kwargs)20except Exception as e:21sys.exit(str(e))24def main():25print("Export only allowed settings:")26settings = run(27["bash", "-c", "grep export ./ci/test/00_setup_env*.sh"],28stdout=subprocess.PIPE,29text=True,30).stdout.splitlines()31settings = set(l.split("=")[0].split("export ")[1] for l in settings)32# Add "hidden" settings, which are never exported, manually. Otherwise,33# they will not be passed on.34settings.update([35"BASE_BUILD_DIR",36"BIND_TEST_ROUTABLE_IPV4",37"BIND_TEST_ROUTABLE_IPV6",38"CI_FAILFAST_TEST_LEAVE_DANGLING",39])41ci_idx = sorted(path.name for path in Path(__file__).parent.glob("00_setup_env_*.sh")).index(Path(os.environ["FILE_ENV"]).name)42subnet_id = ci_idx % 25643ip4_subnet = ipaddress.IPv4Network(f"11.11.{subnet_id}.0/24")44ip4_addr = ip4_subnet[5]45ip6_subnet = ipaddress.IPv6Network(f"1111:1111:{subnet_id:x}::/112")46ip6_addr = ip6_subnet[5]48if not os.getenv("DANGER_RUN_CI_ON_HOST"):49os.environ["BIND_TEST_ROUTABLE_IPV4"] = str(ip4_addr)50os.environ["BIND_TEST_ROUTABLE_IPV6"] = str(ip6_addr)52# Append $USER to /tmp/env to support multi-user systems and $CONTAINER_NAME53# to allow support starting multiple runs simultaneously by the same user.54env_file = "/tmp/env-{u}-{c}".format(55u=os.environ["USER"],56c=os.environ["CONTAINER_NAME"],57)58with open(env_file, "w") as file:59for k, v in os.environ.items():60if k in settings:61file.write(f"{k}={v}\n")62run(["cat", env_file])64if os.getenv("DANGER_RUN_CI_ON_HOST"):65print("Running on host system without docker wrapper")66print("Create missing folders")67for create_dir in [68os.environ["CCACHE_DIR"],69os.environ["PREVIOUS_RELEASES_DIR"],70]:71Path(create_dir).mkdir(parents=True, exist_ok=True)73# Modify PATH to prepend the retry script, needed for CI_RETRY_EXE74os.environ["PATH"] = f"{os.environ['BASE_ROOT_DIR']}/ci/retry:{os.environ['PATH']}"75else:76CI_IMAGE_LABEL = "bitcoin-ci-test"78# Use buildx unconditionally79# Using buildx is required to properly load the correct driver, for use with registry caching. Neither build, nor BUILDKIT=1 currently do this properly80cmd_build = ["docker", "buildx", "build"]81cmd_build += [82f"--file={os.environ['BASE_READ_ONLY_DIR']}/ci/test_imagefile",83f"--build-arg=CI_IMAGE_NAME_TAG={os.environ['CI_IMAGE_NAME_TAG']}",84f"--build-arg=FILE_ENV={os.environ['FILE_ENV']}",85f"--build-arg=BASE_ROOT_DIR={os.environ['BASE_ROOT_DIR']}",86f"--platform={os.environ['CI_IMAGE_PLATFORM']}",87f"--label={CI_IMAGE_LABEL}",88f"--tag={os.environ['CONTAINER_NAME']}",89]90cmd_build += shlex.split(os.getenv("DOCKER_BUILD_CACHE_ARG", ""))91cmd_build += [os.environ["BASE_READ_ONLY_DIR"]]93print(f"Building {os.environ['CONTAINER_NAME']} image tag to run in")94if run(cmd_build, check=False).returncode != 0:95print(f"Retry building {os.environ['CONTAINER_NAME']} image tag after failure")96time.sleep(3)97run(cmd_build)99for suffix in ["ccache", "depends", "depends_sources", "previous_releases"]:100run(["docker", "volume", "create", f"{os.environ['CONTAINER_NAME']}_{suffix}"], check=False)102CI_CCACHE_MOUNT = f"type=volume,src={os.environ['CONTAINER_NAME']}_ccache,dst={os.environ['CCACHE_DIR']}"103CI_DEPENDS_MOUNT = f"type=volume,src={os.environ['CONTAINER_NAME']}_depends,dst={os.environ['DEPENDS_DIR']}/built"104CI_DEPENDS_SOURCES_MOUNT = f"type=volume,src={os.environ['CONTAINER_NAME']}_depends_sources,dst={os.environ['DEPENDS_DIR']}/sources"105CI_PREVIOUS_RELEASES_MOUNT = f"type=volume,src={os.environ['CONTAINER_NAME']}_previous_releases,dst={os.environ['PREVIOUS_RELEASES_DIR']}"106CI_BUILD_MOUNT = []108if os.getenv("DANGER_CI_ON_HOST_FOLDERS"):109# ensure the directories exist110for create_dir in [111os.environ["CCACHE_DIR"],112f"{os.environ['DEPENDS_DIR']}/built",113f"{os.environ['DEPENDS_DIR']}/sources",114os.environ["PREVIOUS_RELEASES_DIR"],115os.environ["BASE_BUILD_DIR"], # Unset by default, must be defined externally116]:117Path(create_dir).mkdir(parents=True, exist_ok=True)119CI_CCACHE_MOUNT = f"type=bind,src={os.environ['CCACHE_DIR']},dst={os.environ['CCACHE_DIR']}"120CI_DEPENDS_MOUNT = f"type=bind,src={os.environ['DEPENDS_DIR']}/built,dst={os.environ['DEPENDS_DIR']}/built"121CI_DEPENDS_SOURCES_MOUNT = f"type=bind,src={os.environ['DEPENDS_DIR']}/sources,dst={os.environ['DEPENDS_DIR']}/sources"122CI_PREVIOUS_RELEASES_MOUNT = f"type=bind,src={os.environ['PREVIOUS_RELEASES_DIR']},dst={os.environ['PREVIOUS_RELEASES_DIR']}"123CI_BUILD_MOUNT = [f"--mount=type=bind,src={os.environ['BASE_BUILD_DIR']},dst={os.environ['BASE_BUILD_DIR']}"]125if os.getenv("DANGER_CI_ON_HOST_CCACHE_FOLDER"):126if not os.path.isdir(os.environ["CCACHE_DIR"]):127print(f"Error: Directory '{os.environ['CCACHE_DIR']}' must be created in advance.")128sys.exit(1)129CI_CCACHE_MOUNT = f"type=bind,src={os.environ['CCACHE_DIR']},dst={os.environ['CCACHE_DIR']}"131network_name = f"{os.environ['CONTAINER_NAME']}-net"132run(["docker", "network", "create", "--ipv6", f"--subnet={ip6_subnet}", f"--subnet={ip4_subnet}", network_name])134if os.getenv("RESTART_CI_DOCKER_BEFORE_RUN"):135print("Restart docker before run to stop and clear all containers started with --rm")136run(["podman", "container", "rm", "--force", "--all"]) # Similar to "systemctl restart docker"138# Still prune everything in case the filtered pruning doesn't work, or if labels were not set139# on a previous run. Belt and suspenders approach, should be fine to remove in the future.140# Prune images used by --external containers (e.g. build containers) when141# using podman.142print("Prune all dangling images")143run(["podman", "image", "prune", "--force", "--external"])145print(f"Prune all dangling {CI_IMAGE_LABEL} images")146# When detecting podman-docker, `--external` should be added.147run(["docker", "image", "prune", "--force", "--filter", f"label={CI_IMAGE_LABEL}"])149cmd_run = ["docker", "run", "--rm", "--interactive", "--detach", "--tty"]150cmd_run += [151"--cap-add=LINUX_IMMUTABLE",152*shlex.split(os.getenv("CI_CONTAINER_CAP", "")),153f"--mount=type=bind,src={os.environ['BASE_READ_ONLY_DIR']},dst={os.environ['BASE_READ_ONLY_DIR']},readonly",154f"--mount={CI_CCACHE_MOUNT}",155f"--mount={CI_DEPENDS_MOUNT}",156f"--mount={CI_DEPENDS_SOURCES_MOUNT}",157f"--mount={CI_PREVIOUS_RELEASES_MOUNT}",158*CI_BUILD_MOUNT,159f"--env-file={env_file}",160f"--name={os.environ['CONTAINER_NAME']}",161f"--network={network_name}",162f"--ip={ip4_addr}",163f"--ip6={ip6_addr}",164f"--platform={os.environ['CI_IMAGE_PLATFORM']}",165os.environ["CONTAINER_NAME"],166]168container_id = run(169cmd_run,170stdout=subprocess.PIPE,171text=True,172).stdout.strip()174def ci_exec(cmd_inner, **kwargs):175if os.getenv("DANGER_RUN_CI_ON_HOST"):176prefix = []177else:178prefix = [179"docker",180"exec",181"--env",182"DANGER_RUN_CI_ON_HOST=1", # Safe to set *inside* the container183container_id,184]186return run([*prefix, *cmd_inner], **kwargs)188# Normalize all folders to BASE_ROOT_DIR189ci_exec([190"rsync",191"--recursive",192"--perms",193"--stats",194"--human-readable",195f"{os.environ['BASE_READ_ONLY_DIR']}/",196f"{os.environ['BASE_ROOT_DIR']}",197])198ci_exec([f"{os.environ['BASE_ROOT_DIR']}/ci/test/01_base_install.sh"])199test_script = f"{os.environ['BASE_ROOT_DIR']}/ci/test/03_test_script.sh"200if os.environ.get("HOST", "").startswith("x86_64-w64-mingw32"):201ci_exec([202"env",203"NIX_BUILD_SHELL=bash",204"nix-shell",205f"{os.environ['BASE_ROOT_DIR']}/contrib/devtools/shell-win64-cross.nix",206"--run",207f"echo 'Windows cross compiler:' && \"$CXX\" -v && exec bash {shlex.quote(test_script)}",208])209else:210ci_exec([test_script])212if not os.getenv("DANGER_RUN_CI_ON_HOST"):213print("Stop and remove CI container by ID")214run(["docker", "container", "kill", container_id])215run(["docker", "network", "rm", network_name])218if __name__ == "__main__":219main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.