close
Skip to content

Commit b65ff0e

Browse files
committed
Merge bitcoin#34548: ci: Add and use ci-windows-cross.py helper
fa13b13 ci: [refactor] Use pathlib over os.path (MarcoFalke) fa2719a ci: [refactor] Move run_unit_tests to ci-windows-cross.py (MarcoFalke) fa99ba5 ci: Set PREVIOUS_RELEASES_DIR env var in ci-windows-cross.py (MarcoFalke) fa4a1ca ci: Move run_functional_tests into ci-windows-cross.py (MarcoFalke) 1111108 ci: [refactor] Move pyzmq install and get_previous_releases into ci-windows-cross.py (MarcoFalke) fac9c7b ci: [refactor] Move config.ini rewrite to ci-windows-cross.py (MarcoFalke) faf7389 ci: Move check_manifests step to ci-windows-cross.py (MarcoFalke) fa674d5 ci: [refactor] Move print_version step into ci-windows-cross.py helper (MarcoFalke) Pull request description: Currently the ci yaml has a mix of Bash and Pwsh snippets, which is problematic: * The `shellcheck` tool does not review the Bash * The ci yaml is not merged with master on re-runs, but the code is, leading to possibly confusing CI errors on re-runs * The Pwsh isn't reviewed at all by any tool * It is tedious to run the CI commands locally on Windows Fix all issues by extracting them into a step-based Python script. ACKs for top commit: janb84: re ACK fa13b13 hebasto: ACK fa13b13, I have reviewed the code and it looks OK. Tree-SHA512: 23d21d3bfb07e102fe1cc15ba5749d553d9766ae6c4a7648bd77df0705469bd138c76a9a2fdeb4d91d3f889a425b7caf25878ecb2e68b604faf9665f8df4eb6d
2 parents 03e5f06 + fa13b13 commit b65ff0e

3 files changed

Lines changed: 182 additions & 59 deletions

File tree

‎.github/ci-windows-cross.py‎

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or https://opensource.org/license/mit/.
5+
6+
import argparse
7+
import os
8+
import shlex
9+
import subprocess
10+
import sys
11+
from pathlib import Path
12+
13+
14+
def run(cmd, **kwargs):
15+
print("+ " + shlex.join(cmd), flush=True)
16+
kwargs.setdefault("check", True)
17+
try:
18+
return subprocess.run(cmd, **kwargs)
19+
except Exception as e:
20+
sys.exit(str(e))
21+
22+
23+
def print_version():
24+
bitcoind = Path.cwd() / "bin" / "bitcoind.exe"
25+
run([str(bitcoind), "-version"])
26+
27+
28+
def check_manifests():
29+
release_dir = Path.cwd() / "bin"
30+
manifest_path = release_dir / "bitcoind.manifest"
31+
32+
cmd_bitcoind_manifest = [
33+
"mt.exe",
34+
"-nologo",
35+
f"-inputresource:{release_dir / 'bitcoind.exe'}",
36+
f"-out:{manifest_path}",
37+
]
38+
run(cmd_bitcoind_manifest)
39+
print(manifest_path.read_text())
40+
41+
skipped = { # Skip as they currently do not have manifests
42+
"fuzz.exe",
43+
"bench_bitcoin.exe",
44+
"test_kernel.exe",
45+
}
46+
for entry in release_dir.iterdir():
47+
if entry.suffix.lower() != ".exe":
48+
continue
49+
if entry.name in skipped:
50+
print(f"Skipping {entry.name} (no manifest present)")
51+
continue
52+
print(f"Checking {entry.name}")
53+
run(["mt.exe", "-nologo", f"-inputresource:{entry}", "-validate_manifest"])
54+
55+
56+
def prepare_tests():
57+
workspace = Path.cwd()
58+
config_path = workspace / "test" / "config.ini"
59+
rpcauth_path = workspace / "share" / "rpcauth" / "rpcauth.py"
60+
replacements = {
61+
"SRCDIR=": f"SRCDIR={workspace}",
62+
"BUILDDIR=": f"BUILDDIR={workspace}",
63+
"RPCAUTH=": f"RPCAUTH={rpcauth_path}",
64+
}
65+
lines = config_path.read_text().splitlines()
66+
for index, line in enumerate(lines):
67+
for prefix, new_value in replacements.items():
68+
if line.startswith(prefix):
69+
lines[index] = new_value
70+
break
71+
content = "\n".join(lines) + "\n"
72+
config_path.write_text(content)
73+
print(content)
74+
previous_releases_dir = Path(os.environ["PREVIOUS_RELEASES_DIR"])
75+
cmd_download_prev_rel = [
76+
sys.executable,
77+
str(workspace / "test" / "get_previous_releases.py"),
78+
"--target-dir",
79+
str(previous_releases_dir),
80+
]
81+
run(cmd_download_prev_rel)
82+
run([sys.executable, "-m", "pip", "install", "pyzmq"])
83+
84+
85+
def run_functional_tests():
86+
workspace = Path.cwd()
87+
num_procs = str(os.process_cpu_count())
88+
test_runner_cmd = [
89+
sys.executable,
90+
str(workspace / "test" / "functional" / "test_runner.py"),
91+
"--jobs",
92+
num_procs,
93+
"--quiet",
94+
f"--tmpdirprefix={workspace}",
95+
"--combinedlogslen=99999999",
96+
*shlex.split(os.environ.get("TEST_RUNNER_EXTRA", "").strip()),
97+
# feature_unsupported_utxo_db.py fails on Windows because of emojis in the test data directory.
98+
"--exclude",
99+
"feature_unsupported_utxo_db.py",
100+
# See https://github.com/bitcoin/bitcoin/issues/31409.
101+
"--exclude",
102+
"wallet_multiwallet.py",
103+
]
104+
run(test_runner_cmd)
105+
106+
# Run feature_unsupported_utxo_db sequentially in ASCII-only tmp dir,
107+
# because it is excluded above due to lack of UTF-8 support in the
108+
# ancient release.
109+
cmd_feature_unsupported_db = [
110+
sys.executable,
111+
str(workspace / "test" / "functional" / "feature_unsupported_utxo_db.py"),
112+
"--previous-releases",
113+
"--tmpdir",
114+
str(Path(workspace) / "test_feature_unsupported_utxo_db"),
115+
]
116+
run(cmd_feature_unsupported_db)
117+
118+
119+
def run_unit_tests():
120+
# Can't use ctest here like other jobs as we don't have a CMake build tree.
121+
commands = [
122+
["./bin/test_bitcoin-qt.exe"],
123+
# Intentionally run sequentially here, to catch test case failures caused by dirty global state from prior test cases:
124+
["./bin/test_bitcoin.exe", "-l", "test_suite"],
125+
["./src/secp256k1/bin/exhaustive_tests.exe"],
126+
["./src/secp256k1/bin/noverify_tests.exe"],
127+
["./src/secp256k1/bin/tests.exe"],
128+
["./src/univalue/object.exe"],
129+
["./src/univalue/unitester.exe"],
130+
]
131+
for cmd in commands:
132+
run(cmd)
133+
134+
135+
def main():
136+
parser = argparse.ArgumentParser(description="Utility to run Windows CI steps.")
137+
steps = [
138+
"print_version",
139+
"check_manifests",
140+
"prepare_tests",
141+
"run_unit_tests",
142+
"run_functional_tests",
143+
]
144+
parser.add_argument("step", choices=steps, help="CI step to perform.")
145+
args = parser.parse_args()
146+
147+
os.environ.setdefault(
148+
"PREVIOUS_RELEASES_DIR",
149+
str(Path.cwd() / "previous_releases"),
150+
)
151+
152+
if args.step == "print_version":
153+
print_version()
154+
elif args.step == "check_manifests":
155+
check_manifests()
156+
elif args.step == "prepare_tests":
157+
prepare_tests()
158+
elif args.step == "run_unit_tests":
159+
run_unit_tests()
160+
elif args.step == "run_functional_tests":
161+
run_functional_tests()
162+
163+
164+
if __name__ == "__main__":
165+
main()

‎.github/ci-windows.py‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def prepare_tests(ci_type):
106106
if ci_type == "standard":
107107
run([sys.executable, "-m", "pip", "install", "pyzmq"])
108108
elif ci_type == "fuzz":
109-
repo_dir = os.path.join(os.getcwd(), "qa-assets")
109+
repo_dir = str(Path.cwd() / "qa-assets")
110110
clone_cmd = [
111111
"git",
112112
"clone",
@@ -120,9 +120,9 @@ def prepare_tests(ci_type):
120120

121121

122122
def run_tests(ci_type):
123-
build_dir = "build"
123+
build_dir = Path.cwd() / "build"
124124
num_procs = str(os.process_cpu_count())
125-
release_bin = os.path.join(os.getcwd(), build_dir, "bin", "Release")
125+
release_bin = build_dir / "bin" / "Release"
126126

127127
if ci_type == "standard":
128128
test_envs = {
@@ -136,12 +136,12 @@ def run_tests(ci_type):
136136
"BITCOINCHAINSTATE": "bitcoin-chainstate.exe",
137137
}
138138
for var, exe in test_envs.items():
139-
os.environ[var] = os.path.join(release_bin, exe)
139+
os.environ[var] = str(release_bin / exe)
140140

141141
ctest_cmd = [
142142
"ctest",
143143
"--test-dir",
144-
build_dir,
144+
str(build_dir),
145145
"--output-on-failure",
146146
"--stop-on-failure",
147147
"-j",
@@ -153,26 +153,26 @@ def run_tests(ci_type):
153153

154154
test_cmd = [
155155
sys.executable,
156-
os.path.join(build_dir, "test", "functional", "test_runner.py"),
156+
str(build_dir / "test" / "functional" / "test_runner.py"),
157157
"--jobs",
158158
num_procs,
159159
"--quiet",
160-
f"--tmpdirprefix={os.getcwd()}",
160+
f"--tmpdirprefix={Path.cwd()}",
161161
"--combinedlogslen=99999999",
162162
*shlex.split(os.environ.get("TEST_RUNNER_EXTRA", "").strip()),
163163
]
164164
run(test_cmd)
165165

166166
elif ci_type == "fuzz":
167-
os.environ["BITCOINFUZZ"] = os.path.join(release_bin, "fuzz.exe")
167+
os.environ["BITCOINFUZZ"] = str(release_bin / "fuzz.exe")
168168
fuzz_cmd = [
169169
sys.executable,
170-
os.path.join(build_dir, "test", "fuzz", "test_runner.py"),
170+
str(build_dir / "test" / "fuzz" / "test_runner.py"),
171171
"--par",
172172
num_procs,
173173
"--loglevel",
174174
"DEBUG",
175-
os.path.join(os.getcwd(), "qa-assets", "fuzz_corpora"),
175+
str(Path.cwd() / "qa-assets" / "fuzz_corpora"),
176176
]
177177
run(fuzz_cmd)
178178

‎.github/workflows/ci.yml‎

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -416,67 +416,25 @@ jobs:
416416
name: ${{ matrix.artifact-name }}-${{ github.run_id }}
417417

418418
- name: Run bitcoind.exe
419-
run: ./bin/bitcoind.exe -version
419+
run: py -3 .github/ci-windows-cross.py print_version
420420

421421
- *SET_UP_VS
422422

423423
- name: Check executable manifests
424-
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
425-
run: |
426-
mt.exe -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
427-
Get-Content bitcoind.manifest
428-
429-
Get-ChildItem -Filter "bin\*.exe" | ForEach-Object {
430-
$exeName = $_.Name
431-
432-
# Skip as they currently do not have manifests
433-
if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe" -or $exeName -eq "test_kernel.exe") {
434-
Write-Host "Skipping $exeName (no manifest present)"
435-
return
436-
}
437-
438-
Write-Host "Checking $exeName"
439-
& mt.exe -nologo -inputresource:$_.FullName -validate_manifest
440-
}
424+
run: py -3 .github/ci-windows-cross.py check_manifests
441425

442426
- name: Run unit tests
443-
# Can't use ctest here like other jobs as we don't have a CMake build tree.
444-
run: |
445-
./bin/test_bitcoin-qt.exe
446-
./bin/test_bitcoin.exe -l test_suite # Intentionally run sequentially here, to catch test case failures caused by dirty global state from prior test cases.
447-
./src/secp256k1/bin/exhaustive_tests.exe
448-
./src/secp256k1/bin/noverify_tests.exe
449-
./src/secp256k1/bin/tests.exe
450-
./src/univalue/object.exe
451-
./src/univalue/unitester.exe
452-
453-
- name: Adjust paths in test/config.ini
454-
shell: pwsh
455-
run: |
456-
(Get-Content "test/config.ini") -replace '(?<=^SRCDIR=).*', '${{ github.workspace }}' -replace '(?<=^BUILDDIR=).*', '${{ github.workspace }}' -replace '(?<=^RPCAUTH=).*', '${{ github.workspace }}/share/rpcauth/rpcauth.py' | Set-Content "test/config.ini"
457-
Get-Content "test/config.ini"
427+
run: py -3 .github/ci-windows-cross.py run_unit_tests
458428

459-
- name: Set previous release directory
429+
- name: Prepare Windows test environment
460430
run: |
461-
echo "PREVIOUS_RELEASES_DIR=${{ runner.temp }}/previous_releases" >> "$GITHUB_ENV"
462-
463-
- name: Get previous releases
464-
run: ./test/get_previous_releases.py --target-dir $PREVIOUS_RELEASES_DIR
431+
py -3 .github/ci-windows-cross.py prepare_tests
465432
466433
- name: Run functional tests
467434
env:
468-
TEST_RUNNER_EXTRA: ${{ github.event_name != 'pull_request' && '--extended' || '' }}
435+
TEST_RUNNER_EXTRA: "--timeout-factor=${{ env.TEST_RUNNER_TIMEOUT_FACTOR }} ${{ case(github.event_name == 'pull_request', '', '--extended') }}"
469436
run: |
470-
py -3 -m pip install pyzmq
471-
py -3 test/functional/test_runner.py --jobs $NUMBER_OF_PROCESSORS --quiet --tmpdirprefix="$RUNNER_TEMP" --combinedlogslen=99999999 --timeout-factor=$TEST_RUNNER_TIMEOUT_FACTOR $TEST_RUNNER_EXTRA \
472-
`# feature_unsupported_utxo_db.py fails on Windows because of emojis in the test data directory.` \
473-
--exclude feature_unsupported_utxo_db.py \
474-
`# See https://github.com/bitcoin/bitcoin/issues/31409.` \
475-
--exclude wallet_multiwallet.py
476-
# Run feature_unsupported_utxo_db sequentially in ASCII-only tmp dir,
477-
# because it is excluded above due to lack of UTF-8 support in the
478-
# ancient release.
479-
py -3 test/functional/feature_unsupported_utxo_db.py --previous-releases --tmpdir="${RUNNER_TEMP}/test_feature_unsupported_utxo_db"
437+
py -3 .github/ci-windows-cross.py run_functional_tests
480438
481439
ci-matrix:
482440
name: ${{ matrix.name }}

0 commit comments

Comments
 (0)