Skip to content

Commit 8ebbdef

Browse files
committed
build: multi-stage builds and testing configuration
- Introduced a new `compose.yml` file for running tests in an isolated Docker environment. - Updated `Dockerfile` to support multi-stage builds, including a dedicated test stage with Bitcoin Core. - Enhanced `install.sh` to allow installation of dependencies only, without building JoinMarket. - Updated documentation to include Docker usage instructions for building images and running tests. build: add version arguments build: update python image tag to 3.13-slim-trixie
1 parent 94660e7 commit 8ebbdef

File tree

6 files changed

+156
-61
lines changed

6 files changed

+156
-61
lines changed

Dockerfile

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
1-
FROM debian:bookworm-slim
1+
ARG BITCOIN_VERSION=29.2
2+
ARG PYTHON_IMAGE_TAG=3.13-slim-trixie
3+
FROM bitcoin/bitcoin:${BITCOIN_VERSION} AS bitcoin
4+
FROM python:${PYTHON_IMAGE_TAG} AS joinmarket
25

3-
RUN mkdir -p /jm/clientserver
46
WORKDIR /jm/clientserver
7+
COPY ./pubkeys ./pubkeys
8+
COPY ./install.sh ./install.sh
9+
10+
RUN apt-get update \
11+
&& apt-get install -y --no-install-recommends \
12+
gnupg \
13+
ca-certificates \
14+
curl \
15+
&& ./install.sh --docker-install --deps-only \
16+
&& apt-get clean \
17+
&& rm -rf /var/lib/apt/lists/*
518

619
COPY . .
720

8-
RUN apt-get update && apt-get install -y --no-install-recommends gnupg ca-certificates=* curl=* \
9-
python3-pip=* python3=* \
10-
&& pip3 config set global.break-system-packages true \
11-
&& pip3 install 'wheel>=0.35.1' \
12-
&& ./install.sh --docker-install \
13-
&& apt-get purge -y --autoremove python3-pip \
21+
RUN apt-get update \
22+
&& ./install.sh --docker-install --no-deps \
1423
&& apt-get clean \
1524
&& rm -rf /var/lib/apt/lists/*
1625

26+
FROM joinmarket AS test
27+
ARG BITCOIN_VERSION
28+
COPY --from=bitcoin /opt/bitcoin-${BITCOIN_VERSION}/bin /usr/local/bin/
29+
RUN ./install.sh --docker-install --no-deps --develop
30+
31+
FROM joinmarket AS final

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ Alternative to this "quickstart": follow the [install guide](docs/INSTALL.md).
6262
* [Installation guide for Qubes+Whonix](https://github.com/qubenix/qubes-whonix-bitcoin/blob/master/1_joinmarket.md).
6363
* [Youtube video installation tutorial for Ubuntu](https://www.youtube.com/watch?v=zTCC86IUzWo).
6464

65+
### Docker
66+
67+
JoinMarket can be built and run using Docker. To build the Docker image:
68+
69+
docker build -t joinmarket .
70+
71+
To run tests using Docker Compose:
72+
73+
docker compose up test
74+
75+
This will run the full test suite in an isolated environment with all necessary dependencies including Bitcoin Core.
76+
6577
### Usage
6678

6779
If you are new, follow and read the links in the [usage guide](docs/USAGE.md).

compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
services:
2+
test:
3+
image: joinmarket
4+
build:
5+
context: .
6+
target: test
7+
args:
8+
BITCOIN_VERSION: 29.2
9+
PYTHON_IMAGE_TAG: 3.13-slim-trixie
10+
volumes:
11+
- ./src:/jm/clientserver/src
12+
- ./test:/jm/clientserver/test
13+
shm_size: '2gb'
14+
entrypoint: ["/bin/sh", "-c"]
15+
command: [". ./jmvenv/bin/activate && exec ./test/run_tests.sh -v"]

docs/INSTALL.md

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,47 @@ There, you need to install the client code (without Joinmarket's bitcoin):
265265
266266
#### Docker Installation
267267
268-
The [Dockerfile](../Dockerfile) provided builds a minimal Docker image which can help in getting started with a custom Docker setup. An example of building and running the [wallet-tool.py](../scripts/wallet-tool.py) script:
268+
The [Dockerfile](../Dockerfile) provided builds an optimized multi-stage Docker image for JoinMarket. The build process is optimized for layer caching and includes all necessary dependencies.
269269
270-
```
271-
docker build -t joinmarket-test ./
272-
docker run --rm -it joinmarket-test bash -c "cd scripts && python3 wallet-tool.py --help"
273-
```
270+
##### Building the Docker image
271+
272+
To build the production image:
273+
274+
docker build -t joinmarket .
275+
276+
This creates a minimal production image with JoinMarket installed.
277+
278+
##### Running JoinMarket scripts in Docker
279+
280+
Example of running the [wallet-tool.py](../scripts/wallet-tool.py) script:
281+
282+
docker run --rm -it joinmarket bash -c "source jmvenv/bin/activate && cd scripts && python wallet-tool.py --help"
283+
284+
##### Running tests with Docker Compose
285+
286+
The repository includes a `compose.yml` file for easy testing:
287+
288+
docker compose up test
289+
290+
This will:
291+
292+
* Build a test image that includes Bitcoin Core 29.2
293+
* Mount the `src` and `test` directories for development
294+
* Run the full test suite with all dependencies configured automatically
295+
* Use shared memory for improved test performance
296+
297+
##### Building custom images
298+
299+
The Dockerfile uses multi-stage builds with the following targets:
300+
301+
* `joinmarket` (default) - Production image with JoinMarket installed
302+
* `test` - Testing image that includes Bitcoin Core binaries
303+
304+
You can build a specific target:
305+
306+
docker build --target test -t joinmarket:test .
274307
275-
A new Docker image can be built using `joinmarket-test` as a base using `FROM joinmarket-test`. See [Docker documentation](https://docs.docker.com/engine/reference/builder/) for more details.
308+
See [Docker documentation](https://docs.docker.com/engine/reference/builder/) for more details on multi-stage builds.
276309
277310
#### Development (or making other changes to the code)
278311

docs/TESTING.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
### Test instructions (for developers):
22

3+
#### Quick start: Running tests with Docker
4+
5+
The easiest way to run the full test suite is using Docker Compose, which handles all dependencies automatically:
6+
7+
docker compose run --rm test
8+
9+
This will:
10+
11+
* Build a Docker image with all dependencies (Python, Bitcoin Core 29.2, miniircd)
12+
* Run the complete test suite in an isolated environment
13+
* Automatically download and set up miniircd
14+
* No need to install bitcoind or other dependencies on your host machine
15+
16+
For development, the Docker setup mounts the `src` and `test` directories, so you can make changes locally and re-run tests without rebuilding the image.
17+
18+
#### Manual setup
19+
320
Work in your `jmvenv` virtual environment as for all Joinmarket work. Make sure to have [bitcoind](https://bitcoin.org/en/full-node) 29.0 or newer installed. Also need miniircd installed to the root (i.e. in your `joinmarket-clientserver` directory):
421

522
(jmvenv)$ cd /path/to/joinmarket-clientserver

install.sh

Lines changed: 50 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,9 @@ CookieAuthentication 1
415415

416416
joinmarket_install ()
417417
{
418+
if [[ ${deps_only} == 1 ]]; then
419+
return 0
420+
fi
418421
reqs='services'
419422

420423
if [[ ${with_qt} == "1" ]]; then
@@ -424,8 +427,7 @@ joinmarket_install ()
424427
reqs+=',test'
425428
fi
426429

427-
if [ "$with_jmvenv" == 1 ]; then pip_command=pip; else pip_command=pip3; fi
428-
$pip_command install -e ".[${reqs}]" || return 1
430+
pip install -e ".[${reqs}]" || return 1
429431

430432
if [[ ${with_qt} == "1" ]]; then
431433
if [[ -d ~/.local/share/icons ]] && [[ -d ~/.local/share/applications ]]; then
@@ -484,7 +486,12 @@ parse_flags ()
484486
;;
485487
--docker-install)
486488
with_sudo='0'
487-
with_jmvenv='0'
489+
;;
490+
--deps-only)
491+
deps_only='1'
492+
;;
493+
--no-deps)
494+
no_deps='1'
488495
;;
489496
"")
490497
break
@@ -507,6 +514,8 @@ Options:
507514
--with-local-tor build Tor locally and autostart when needed
508515
--with-qt build the Qt GUI
509516
--without-qt don't build the Qt GUI
517+
--deps-only only install dependencies, do not build or install Joinmarket
518+
--no-deps do not install dependencies
510519
"
511520
return 1
512521
;;
@@ -568,22 +577,19 @@ main ()
568577
use_os_deps_check='1'
569578
use_secp_check='1'
570579
with_qt=''
571-
with_jmvenv='1'
572580
with_sudo='1'
573581
reinstall='false'
582+
deps_only='0'
583+
no_deps='0'
574584
if ! parse_flags "${@}"; then
575585
return 1
576586
fi
577587

578588
jm_source="$PWD"
579-
if [ "$with_jmvenv" == 1 ]; then
580-
jm_root="${jm_source}/jmvenv"
581-
export PKG_CONFIG_PATH="${jm_root}/lib/pkgconfig:${PKG_CONFIG_PATH}"
582-
export LD_LIBRARY_PATH="${jm_root}/lib:${LD_LIBRARY_PATH}"
583-
export C_INCLUDE_PATH="${jm_root}/include:${C_INCLUDE_PATH}"
584-
else
585-
jm_root=""
586-
fi
589+
jm_root="${jm_source}/jmvenv"
590+
export PKG_CONFIG_PATH="${jm_root}/lib/pkgconfig:${PKG_CONFIG_PATH}"
591+
export LD_LIBRARY_PATH="${jm_root}/lib:${LD_LIBRARY_PATH}"
592+
export C_INCLUDE_PATH="${jm_root}/include:${C_INCLUDE_PATH}"
587593

588594
# os check
589595
install_os="$( install_get_os )"
@@ -595,56 +601,53 @@ main ()
595601

596602
MAKEFLAGS="-j $(num_cores)" && export MAKEFLAGS
597603

598-
if [ "$with_jmvenv" == 1 ]; then
599-
if ! venv_setup; then
600-
echo "Joinmarket Python virtual environment could not be setup. Exiting."
601-
return 1
602-
fi
603-
# shellcheck source=/dev/null
604-
source "${jm_root}/bin/activate"
605-
else
606-
upgrade_setuptools
604+
if ! venv_setup; then
605+
echo "Joinmarket Python virtual environment could not be setup. Exiting."
606+
return 1
607607
fi
608+
# shellcheck source=/dev/null
609+
source "${jm_root}/bin/activate"
608610
if [[ ${build_local_tor} == "1" ]]; then
609611
if ! tor_deps_install; then
610612
echo "Tor dependencies could not be installed. Exiting."
611613
return 1
612614
fi
613615
fi
614-
mkdir -p "deps/cache"
615-
pushd deps || return 1
616-
if ! libsecp256k1_install; then
617-
echo "libsecp256k1 was not built. Exiting."
618-
return 1
619-
fi
620-
if ! libffi_install; then
621-
echo "Libffi was not built. Exiting."
622-
return 1
623-
fi
624-
if ! libsodium_install; then
625-
echo "Libsodium was not built. Exiting."
626-
return 1
627-
fi
628-
if [[ ${build_local_tor} == "1" ]]; then
629-
if ! tor_install; then
630-
echo "Building local Tor was requested, but not built. Exiting."
616+
617+
if [[ ${no_deps} == 0 ]]; then
618+
mkdir -p "deps/cache"
619+
pushd deps || return 1
620+
if ! libsecp256k1_install; then
621+
echo "libsecp256k1 was not built. Exiting."
622+
return 1
623+
fi
624+
if ! libffi_install; then
625+
echo "Libffi was not built. Exiting."
626+
return 1
627+
fi
628+
if ! libsodium_install; then
629+
echo "Libsodium was not built. Exiting."
631630
return 1
632631
fi
632+
if [[ ${build_local_tor} == "1" ]]; then
633+
if ! tor_install; then
634+
echo "Building local Tor was requested, but not built. Exiting."
635+
return 1
636+
fi
637+
fi
638+
popd || return 1
633639
fi
634-
popd || return 1
635640
if ! joinmarket_install; then
636641
echo "Joinmarket was not installed. Exiting."
637-
if [ "$with_jmvenv" == 1 ]; then deactivate; fi
642+
deactivate
638643
return 1
639644
fi
640-
if [ "$with_jmvenv" == 1 ]; then
641-
deactivate
642-
echo "Joinmarket successfully installed
643-
Before executing scripts or tests, run:
645+
deactivate
646+
echo "Joinmarket successfully installed
647+
Before executing scripts or tests, run:
644648
645-
\`source jmvenv/bin/activate\`
649+
\`source jmvenv/bin/activate\`
646650
647-
from this directory, to activate the virtual environment."
648-
fi
651+
from this directory, to activate the virtual environment."
649652
}
650653
main "${@}"

0 commit comments

Comments
 (0)