Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 114 additions & 31 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ on:
type: string
required: true
default: main
rust_image:
description: "The rust base image tag (e.g. 1.90.0-slim-bookworm). Defaults to latest."
type: string
required: false
default: latest
release:
types: [published]

Expand All @@ -19,6 +24,7 @@ defaults:
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- runs-on: ubuntu-latest
Expand All @@ -29,42 +35,82 @@ jobs:
permissions:
contents: read
steps:
# The workflow's own ref provides Dockerfile + entrypoint.sh + docker/.
# The binary's source is cloned by the Dockerfile itself based on CLI_REF.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

# NOTE: switch fallback ref to `main` once this branch lands.
- name: Fall back to docker-custom-image packaging files if missing
run: |
if [[ ! -f Dockerfile ]]; then
echo "::notice::Dockerfile not present at ${GITHUB_REF}; falling back to docker-custom-image"
git fetch --depth=1 origin docker-custom-image
git checkout FETCH_HEAD -- Dockerfile .dockerignore entrypoint.sh docker/
fi

- name: Resolve build args
run: |
rust_image="${{ github.event_name == 'workflow_dispatch' && inputs.rust_image || 'latest' }}"
cli_ref="${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref_name }}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[picky] we might want to resolve the input ref into a commit sha before the build job. Technically this could race condition if a branch is passed and the branch is updated between amd64 run and arm64 run?

Though this isn't an issue when running on publish so am fine ignoring.

echo "RUST_IMAGE=${rust_image}" >> "$GITHUB_ENV"
echo "CLI_REF=${cli_ref}" >> "$GITHUB_ENV"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4

- name: Log in to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libudev-dev libdbus-1-dev
- id: build
name: Build and push by digest
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: .
platforms: linux/${{ matrix.arch }}
outputs: type=image,name=stellar/stellar-cli,push-by-digest=true,name-canonical=true,push=true
build-args: |
RUST_IMAGE=${{ env.RUST_IMAGE }}
CLI_REF=${{ env.CLI_REF }}

- name: Build binary
run: cargo build --package stellar-cli --release
- name: Export digest
run: |
mkdir -p "${RUNNER_TEMP}/digests"
digest="${{ steps.build.outputs.digest }}"
touch "${RUNNER_TEMP}/digests/${digest#sha256:}"

- name: Upload binary
- name: Upload digest
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: stellar-${{ matrix.arch }}
path: target/release/stellar
name: digest-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1

docker:
manifest:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
fetch-depth: 0

- name: Download binaries
# NOTE: switch fallback ref to `main` once this branch lands.
- name: Ensure docker/README.md is available
run: |
if [[ ! -f docker/README.md ]]; then
git fetch --depth=1 origin docker-custom-image
git checkout FETCH_HEAD -- docker/
fi

- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: stellar-*
merge-multiple: false

- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
path: ${{ runner.temp }}/digests
pattern: digest-*
merge-multiple: true

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
Expand All @@ -75,31 +121,68 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

# Compute Docker tags from the ref.
# - Version tag (e.g. v1.2.3): push versioned + latest tags.
# - Any other ref: push a tag for the resolved commit SHA.
- name: Resolve rust image
run: |
rust_image="${{ github.event_name == 'workflow_dispatch' && inputs.rust_image || 'latest' }}"
echo "RUST_IMAGE=${rust_image}" >> "$GITHUB_ENV"

Comment thread
fnando marked this conversation as resolved.
- name: Resolve build commit SHA
run: |
ref="${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref_name }}"
if [[ "$ref" =~ ^[0-9a-f]{40}$ ]]; then
sha="$ref"
else
sha="$(git ls-remote https://github.com/${{ github.repository }}.git "$ref" | awk 'END{print $1}')"
fi
if [[ -z "$sha" ]]; then
echo "::error::Could not resolve SHA for ref: $ref"
exit 1
fi
echo "BUILD_SHA=${sha}" >> "$GITHUB_ENV"

# Compute Docker tags from the ref and rust image.
# - Version tag (e.g. v1.2.3) + default rust: push versioned + latest tags.
# - Version tag + custom rust: push versioned + -rust-<image> suffix only (do not update :latest).
# - Any other ref: push a tag for the resolved commit SHA, with the rust suffix when custom.
- name: Compute tags
run: |
ref="${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref_name }}"

suffix=""
if [[ "${RUST_IMAGE}" != "latest" ]]; then
sanitized="${RUST_IMAGE//[^A-Za-z0-9._-]/-}"
suffix="-rust-${sanitized}"
fi

if [[ "$ref" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
version="${ref#v}"
echo "DOCKER_TAGS=stellar/stellar-cli:${version},stellar/stellar-cli:latest" >> $GITHUB_ENV
if [[ -n "$suffix" ]]; then
echo "DOCKER_TAGS=stellar/stellar-cli:${version}${suffix}" >> $GITHUB_ENV
else
echo "DOCKER_TAGS=stellar/stellar-cli:${version},stellar/stellar-cli:latest" >> $GITHUB_ENV
fi
elif [[ "${{ github.event_name }}" == "release" ]]; then
echo "::error::Release tag '${ref}' is not a valid version tag (expected vX.Y.Z)."
exit 1
else
commit="$(git rev-parse HEAD)"
echo "DOCKER_TAGS=stellar/stellar-cli:${commit}" >> $GITHUB_ENV
echo "DOCKER_TAGS=stellar/stellar-cli:${BUILD_SHA}${suffix}" >> $GITHUB_ENV
fi

- name: Build and push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.DOCKER_TAGS }}
- name: Create and push manifest list
working-directory: ${{ runner.temp }}/digests
run: |
IFS=',' read -ra TAGS <<< "${DOCKER_TAGS}"
tag_args=()
for tag in "${TAGS[@]}"; do
tag_args+=(-t "$tag")
done

digest_refs=()
for f in *; do
digest_refs+=("stellar/stellar-cli@sha256:${f}")
done

docker buildx imagetools create "${tag_args[@]}" "${digest_refs[@]}"

- name: Update Docker Hub description
run: |
Expand Down
26 changes: 22 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
FROM rust:latest
ARG RUST_IMAGE=latest

FROM rust:${RUST_IMAGE} AS builder

RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential cmake pkg-config git \
libssl-dev libudev-dev libdbus-1-dev && \
rm -rf /var/lib/apt/lists/*

ARG CLI_REPO=https://github.com/stellar/stellar-cli
ARG CLI_REF=main

WORKDIR /src
RUN git clone "${CLI_REPO}" . && \
git checkout "${CLI_REF}"
Comment on lines +15 to +16
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want to checkout via git from the dockerfile. This is somewhat misleading for users who build with the dockerfile locally.

e.g. as it sits docker build ./ will build from main instead of local.

IMO - we would be better off doing the following:

  1. Have the github action checkout the code
  2. Have the dockerfile do COPY . /src, and keep the builder step to actually build the binary
  3. keep the image creation step as is

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the dockerfile will be extracted into its own repo, as per this thread.

But I believe the proper fix is actually just downloading the binary distributed through the release pages, because re-building the cli inside/outside the dockerfile is changing the cliver metadata anyway. I'm just confirming if we want to move into that direction before doing the work.


RUN cargo build --package stellar-cli --release

FROM rust:${RUST_IMAGE}

RUN rustup target add wasm32v1-none

RUN apt-get update && \
apt-get install -y --no-install-recommends libudev1 libssl3 && \
apt-get install -y --no-install-recommends libudev1 libssl3 libdbus-1-3 && \
rm -rf /var/lib/apt/lists/*

ARG TARGETARCH
COPY stellar-${TARGETARCH}/stellar /usr/local/bin/stellar
COPY --from=builder /src/target/release/stellar /usr/local/bin/stellar
RUN chmod +x /usr/local/bin/stellar

ENV STELLAR_CONFIG_HOME=/config
Expand Down
Loading