diff --git a/.github/workflows/imagebuild.yml b/.github/workflows/imagebuild.yml new file mode 100644 index 000000000..48f2a4329 --- /dev/null +++ b/.github/workflows/imagebuild.yml @@ -0,0 +1,72 @@ +name: Build BitcoinCore tank image and push to DockerHub + +on: + push: + branches: + - main + +jobs: + detect-new-tags: + runs-on: ubuntu-latest + outputs: + new_tags: ${{ steps.diff.outputs.new_tags }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect new tags + id: diff + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + # Compare PR head against base branch + git fetch origin "${{ github.base_ref }}" --depth=1 + DIFF_BASE="origin/${{ github.base_ref }}" + else + # Compare push against previous commit + DIFF_BASE="${{ github.event.before }}" + fi + + new_tags=$(git diff --unified=0 "$DIFF_BASE" HEAD -- src/warnet/bitcoincore.tags \ + | grep '^+[^+]' \ + | sed 's/^+//') + + echo "new_tags<> $GITHUB_OUTPUT + echo "$new_tags" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + build-images: + needs: detect-new-tags + if: needs.detect-new-tags.outputs.new_tags != '' + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push multi-platform images + run: | + for tag in ${{ needs.detect-new-tags.outputs.new_tags }}; do + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --build-arg REPO=bitcoin/bitcoin \ + --build-arg COMMIT_SHA=v$tag \ + --build-arg BUILD_ARGS='-DBUILD_TESTS=OFF -DBUILD_GUI=OFF -DBUILD_BENCH=OFF -DBUILD_UTIL=ON -DBUILD_FUZZ_BINARY=OFF -DWITH_ZMQ=ON' \ + -t bitcoindevproject/bitcoin:$tag \ + --file resources/images/bitcoin/Dockerfile.dev \ + resources/images/bitcoin/ \ + --push + done diff --git a/resources/images/bitcoin/Dockerfile.dev b/resources/images/bitcoin/Dockerfile.dev index 7fd84537b..97ee6eb2f 100644 --- a/resources/images/bitcoin/Dockerfile.dev +++ b/resources/images/bitcoin/Dockerfile.dev @@ -32,12 +32,27 @@ FROM deps AS build ENV BITCOIN_PREFIX=/opt/bitcoin WORKDIR /build +# Assume $COMMIT_SHA is a git commit but if that fails, then +# assume it is a release tag that can be checked out RUN set -ex \ && cd /build \ - && git clone --depth 1 "https://github.com/${REPO}" \ - && cd bitcoin \ - && git fetch --depth 1 origin "$COMMIT_SHA" \ - && git checkout "$COMMIT_SHA" \ + && git init \ + && git remote add origin "https://github.com/${REPO}" \ + + # Try commit/branch first + && if git fetch --depth 1 origin "$COMMIT_SHA:refs/temp/ref" 2>/dev/null; then \ + REF=refs/temp/ref; \ + else \ + + # Fallback: fetch tag explicitly into local refs + git fetch --depth 1 origin "refs/tags/$COMMIT_SHA:refs/tags/$COMMIT_SHA"; \ + REF="refs/tags/$COMMIT_SHA"; \ + fi \ + + # Resolve tag -> commit if needed + && resolved=$(git rev-parse --verify "$REF^{commit}") \ + && git checkout "$resolved" \ + && git apply /tmp/isroutable.patch \ && git apply /tmp/addrman.patch \ && sed -i s:sys/fcntl.h:fcntl.h: src/compat/compat.h \ diff --git a/src/warnet/bitcoincore.tags b/src/warnet/bitcoincore.tags new file mode 100644 index 000000000..547a5822c --- /dev/null +++ b/src/warnet/bitcoincore.tags @@ -0,0 +1,10 @@ +22.2 +23.2 +24.2 +25.1 +26.0 +27.0 +28.1 +29.0 +30.2 +31.0 diff --git a/src/warnet/constants.py b/src/warnet/constants.py index 19c75599c..88da10358 100644 --- a/src/warnet/constants.py +++ b/src/warnet/constants.py @@ -3,14 +3,11 @@ from importlib.resources import files from pathlib import Path -# Constants used throughout the project -# Storing as constants for now but we might want a more sophisticated config management -# at some point. -SUPPORTED_TAGS = ["29.0", "28.1", "27.0", "26.0", "25.1", "24.2", "23.2", "22.2"] +SUPPORTED_TAGS = [] +tags_file_path = Path(__file__).with_name("bitcoincore.tags") +with tags_file_path.open() as f: + SUPPORTED_TAGS = [line.strip() for line in f if line.strip()][::-1] DEFAULT_TAG = SUPPORTED_TAGS[0] -WEIGHTED_TAGS = [ - tag for index, tag in enumerate(reversed(SUPPORTED_TAGS)) for _ in range(index + 1) -] DEFAULT_NAMESPACE = "default" LOGGING_NAMESPACE = "warnet-logging" diff --git a/src/warnet/image.py b/src/warnet/image.py index a13fe8e6e..8119f0ba9 100644 --- a/src/warnet/image.py +++ b/src/warnet/image.py @@ -23,14 +23,16 @@ def image(): @click.option("--arches", required=False, type=str) @click.option("--action", required=False, type=str, default="load") def build(repo, commit_sha, tags, build_args, arches, action): - """Build a Bitcoin Core Docker image with specified parameters. + """Build a Bitcoin Core Docker image with specified parameters from specified commit or tag. \b Usage Examples: # Build an image for Warnet repository - warnet image build --repo bitcoin/bitcoin --commit-sha d6db87165c6dc2123a759c79ec236ea1ed90c0e3 --tags bitcoindevproject/bitcoin:v29.0-rc2 --arches amd64,arm64,armhf --action push - # Build an image for local testing - warnet image build --repo bitcoin/bitcoin --commit-sha d6db87165c6dc2123a759c79ec236ea1ed90c0e3 --tags bitcoindevproject/bitcoin:v29.0-rc2 --action load + warnet image build --repo bitcoin/bitcoin --commit-sha d6db87165c6dc2123a759c79ec236ea1ed90c0e3 --tags bitcoindevproject/bitcoin:v29.0-rc2 --action push + # Build from a tag instead of commit hash + warnet image build --repo bitcoin/bitcoin --commit-sha v31.0 --tags bitcoindevproject/bitcoin:v31.0 --action push + # Build an image for local testing on arm64 only + warnet image build --repo bitcoin/bitcoin --commit-sha d6db87165c6dc2123a759c79ec236ea1ed90c0e3 --tags bitcoindevproject/bitcoin:v29.0-rc2 --arches arm64 --action load """ res = build_image(repo, commit_sha, tags, build_args, arches, action) if not res: diff --git a/src/warnet/image_build.py b/src/warnet/image_build.py index b71be6d34..042315bd9 100644 --- a/src/warnet/image_build.py +++ b/src/warnet/image_build.py @@ -1,7 +1,7 @@ import subprocess from importlib.resources import files -ARCHES = ["amd64", "arm64", "armhf"] +ARCHES = ["amd64", "arm64"] dockerfile_path = files("resources.images.bitcoin").joinpath("Dockerfile.dev") @@ -29,15 +29,10 @@ def build_image( build_arches = [] if not arches: - build_arches.append("amd64") + build_arches = ARCHES else: build_arches.extend(arches.split(",")) - for arch in build_arches: - if arch not in ARCHES: - print(f"Error: {arch} is not a supported architecture") - return False - print(f"{repo=:}") print(f"{commit_sha=:}") print(f"{tags=:}")