From 04997559b6172bafb2420c60726297f2b14b2035 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 18 Jan 2024 19:21:51 -0500 Subject: [PATCH] Add Puffin Docker image --- .dockerignore | 2 - .github/workflows/build-binaries.yml | 2 +- .github/workflows/build-docker.yml | 71 +++++++++++++++++++ .github/workflows/release.yml | 10 +++ Cargo.toml | 2 +- Dockerfile | 45 ++++++++++++ .../puffin-dev/builder.dockerfile | 2 +- 7 files changed, 129 insertions(+), 5 deletions(-) delete mode 100644 .dockerignore create mode 100644 .github/workflows/build-docker.yml create mode 100644 Dockerfile rename builder.dockerfile => crates/puffin-dev/builder.dockerfile (94%) diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 4a8659a479d7..000000000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!rust-toolchain.toml diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml index 776803bec2b5..cb5024b811c2 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-binaries.yml @@ -17,7 +17,7 @@ on: # When we change pyproject.toml, we want to ensure that the maturin builds still work. - pyproject.toml # And when we change this workflow itself... - - .github/workflows/*.yml + - .github/workflows/build-binaries.yml concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml new file mode 100644 index 000000000000..517b88648b07 --- /dev/null +++ b/.github/workflows/build-docker.yml @@ -0,0 +1,71 @@ +# Build and publish a Docker image. +# +# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a local +# artifacts job within `cargo-dist`. +# +# TODO(charlie): Ideally, the publish step would happen as a publish job within `cargo-dist`, but +# sharing the built image as an artifact between jobs is challenging. +name: "[puffin] Build Docker image" + +on: + workflow_call: + inputs: + plan: + required: true + type: string + pull_request: + paths: + # When we change pyproject.toml, we want to ensure that the maturin builds still work. + - pyproject.toml + # And when we change this workflow itself... + - .github/workflows/build-docker.yml + +jobs: + docker-publish: + name: Build Docker image (ghcr.io/astral-sh/puffin) + runs-on: ubuntu-latest + environment: + name: release + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/astral-sh/puffin + + - name: Check tag consistency + if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} + run: | + version=$(grep "version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g') + if [ "${{ fromJson(inputs.plan).announcement_tag }}" != "${version}" ]; then + echo "The input tag does not match the version from pyproject.toml:" >&2 + echo "${{ fromJson(inputs.plan).announcement_tag }}" >&2 + echo "${version}" >&2 + exit 1 + else + echo "Releasing ${version}" + fi + + - name: "Build and push Docker image" + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + # Reuse the builder + cache-from: type=gha + cache-to: type=gha,mode=max + push: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} + tags: ghcr.io/astral-sh/puffin:latest,ghcr.io/astral-sh/puffin:${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || 'dry-run' }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f6dcd11b92a..91cd22e3ed2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,11 +93,21 @@ jobs: plan: ${{ needs.plan.outputs.val }} secrets: inherit + custom-build-docker: + needs: + - plan + if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} + uses: ./.github/workflows/build-docker.yml + with: + plan: ${{ needs.plan.outputs.val }} + secrets: inherit + # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - custom-build-binaries + - custom-build-docker runs-on: "ubuntu-20.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index a8229d58a9ab..0805364d3083 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,6 +164,6 @@ dispatch-releases = true # Whether CI should include auto-generated code to build local artifacts build-local-artifacts = false # Local artifacts jobs to run in CI -local-artifacts-jobs = ["./build-binaries"] +local-artifacts-jobs = ["./build-binaries", "./build-docker"] # Publish jobs to run in CI publish-jobs = ["./publish-pypi"] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000000..16be9504357a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +FROM --platform=$BUILDPLATFORM ubuntu as build +ENV HOME="/root" +WORKDIR $HOME + +RUN apt update \ + && apt install -y --no-install-recommends \ + build-essential \ + curl \ + python3-venv \ + cmake \ + && apt clean \ + && rm -rf /var/lib/apt/lists/* + +# Setup zig as cross compiling linker +RUN python3 -m venv $HOME/.venv +RUN .venv/bin/pip install cargo-zigbuild +ENV PATH="$HOME/.venv/bin:$PATH" + +# Install rust +ARG TARGETPLATFORM +RUN case "$TARGETPLATFORM" in \ + "linux/arm64") echo "aarch64-unknown-linux-musl" > rust_target.txt ;; \ + "linux/amd64") echo "x86_64-unknown-linux-musl" > rust_target.txt ;; \ + *) exit 1 ;; \ + esac +# Update rustup whenever we bump the rust version +COPY rust-toolchain.toml rust-toolchain.toml +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --target $(cat rust_target.txt) --profile minimal --default-toolchain none +ENV PATH="$HOME/.cargo/bin:$PATH" +# Installs the correct toolchain version from rust-toolchain.toml and then the musl target +RUN rustup target add $(cat rust_target.txt) + +# Build +COPY crates crates +COPY ./Cargo.toml Cargo.toml +COPY ./Cargo.lock Cargo.lock +RUN cargo zigbuild --bin puffin --target $(cat rust_target.txt) --release +RUN cp target/$(cat rust_target.txt)/release/puffin /puffin +# TODO(konsti): Optimize binary size, with a version that also works when cross compiling +# RUN strip --strip-all /puffin + +FROM scratch +COPY --from=build /puffin /puffin +WORKDIR /io +ENTRYPOINT ["/puffin"] diff --git a/builder.dockerfile b/crates/puffin-dev/builder.dockerfile similarity index 94% rename from builder.dockerfile rename to crates/puffin-dev/builder.dockerfile index a1ab9476898c..b6bedaf24889 100644 --- a/builder.dockerfile +++ b/crates/puffin-dev/builder.dockerfile @@ -23,5 +23,5 @@ WORKDIR /app RUN python3 -m venv $HOME/venv-docker ENV VIRTUAL_ENV="$HOME/venv-docker" ENV PATH="$HOME/.cargo/bin:$HOME/venv-docker/bin:$PATH" -COPY rust-toolchain.toml rust-toolchain.toml +RUN rustup default 1.75.0 RUN rustup show