Skip to content
This repository was archived by the owner on Dec 16, 2022. It is now read-only.

Commit ae85456

Browse files
authored
Merge pull request #10 from pjbgf/static
2 parents 70c676b + 12147cc commit ae85456

14 files changed

+568
-115
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
build/
2+
vendor/

Dockerfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM scratch
22

3-
COPY ./hack/Makefile Makefile
3+
COPY ./hack/Makefile /Makefile
4+
COPY ./hack/static.sh /static.sh

Dockerfile.test

+102-29
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,126 @@
11
# This Dockerfile tests the hack/Makefile output against git2go.
2-
ARG BASE_VARIANT=bullseye
2+
ARG BASE_VARIANT=alpine
33
ARG GO_VERSION=1.17.6
44
ARG XX_VERSION=1.1.0
55

66
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
77

8-
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} as gostable
8+
FROM golang:${GO_VERSION}-${BASE_VARIANT} as gostable
99

1010
FROM gostable AS go-linux
1111

12-
FROM go-${TARGETOS} AS build-base-bullseye
12+
FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-deps
13+
14+
RUN apk add --no-cache \
15+
bash \
16+
curl \
17+
build-base \
18+
linux-headers \
19+
perl \
20+
cmake \
21+
pkgconfig \
22+
gcc \
23+
musl-dev \
24+
clang \
25+
lld
1326

1427
COPY --from=xx / /
1528

16-
# Align golang base image with bookworm.
17-
# TODO: Replace this with a golang bookworm variant, once it is released.
18-
RUN echo "deb http://deb.debian.org/debian bookworm main" > /etc/apt/sources.list.d/bookworm.list \
19-
&& echo "deb-src http://deb.debian.org/debian bookworm main" /etc/apt/sources.list.d/bookworm.list \
20-
&& xx-apt update \
21-
&& xx-apt -t bookworm upgrade -y
29+
ARG TARGETPLATFORM
2230

23-
COPY ./hack/Makefile /libgit2/Makefile
31+
RUN xx-apk add --no-cache \
32+
xx-c-essentials
2433

25-
RUN make -C /libgit2/ cmake
34+
RUN xx-apk add --no-cache \
35+
xx-cxx-essentials
2636

2737
ARG TARGETPLATFORM
28-
RUN make -C /libgit2/ dependencies
38+
RUN xx-apk add --no-cache \
39+
build-base \
40+
pkgconfig \
41+
gcc \
42+
musl-dev \
43+
clang \
44+
lld \
45+
llvm \
46+
linux-headers
2947

30-
FROM build-base-${BASE_VARIANT} as libgit2-bullseye
48+
WORKDIR /build
49+
COPY hack/static.sh .
3150

3251
ARG TARGETPLATFORM
33-
RUN FLAGS=$(xx-clang --print-cmake-defines) make -C /libgit2/ libgit2
52+
ENV CC=xx-clang
53+
ENV CXX=xx-clang++
54+
55+
RUN CHOST=$(xx-clang --print-target-triple) \
56+
./static.sh build_libz
57+
58+
RUN CHOST=$(xx-clang --print-target-triple) \
59+
./static.sh build_openssl
60+
61+
RUN export LIBRARY_PATH="/usr/local/$(xx-info triple)/lib:/usr/local/$(xx-info triple)/lib64:${LIBRARY_PATH}" && \
62+
export PKG_CONFIG_PATH="/usr/local/$(xx-info triple)/lib/pkgconfig:/usr/local/$(xx-info triple)/lib64/pkgconfig" && \
63+
export OPENSSL_ROOT_DIR="/usr/local/$(xx-info triple)" && \
64+
export OPENSSL_CRYPTO_LIBRARY="/usr/local/$(xx-info triple)/lib64" && \
65+
export OPENSSL_INCLUDE_DIR="/usr/local/$(xx-info triple)/include/openssl"
66+
67+
RUN ./static.sh build_libssh2
68+
RUN ./static.sh build_libgit2
69+
70+
71+
FROM go-${TARGETOS} AS build
72+
73+
# Copy cross-compilation tools
74+
COPY --from=xx / /
75+
# Copy compiled libraries
76+
COPY --from=build-deps /usr/local/ /usr/local/
3477

35-
FROM libgit2-${BASE_VARIANT} as test-bullseye
78+
RUN apk add clang lld pkgconfig
3679

37-
# Cache clone
38-
ARG GIT2GO_TAG
39-
RUN git config --global advice.detachedHead false \
40-
&& git clone --depth=1 --branch=${GIT2GO_TAG} https://github.com/libgit2/git2go /git2go \
41-
&& cd /git2go \
42-
&& go mod tidy
80+
WORKDIR /root/smoketest
81+
COPY tests/smoketest/go.mod .
82+
COPY tests/smoketest/go.sum .
83+
RUN go mod download
4384

44-
# Set workdir
45-
WORKDIR /git2go
85+
COPY tests/smoketest/main.go .
4686

47-
# Run tests
4887
ARG TARGETPLATFORM
49-
ARG CACHE_BUST
50-
RUN set -eux; \
51-
echo "=> Dynamic test at $CACHE_BUST for $TARGETPLATFORM" \
52-
&& CGO_ENABLED=1 xx-go run script/check-MakeGitError-thread-lock.go \
53-
&& CGO_ENABLED=1 xx-go test ./...
88+
# Some dependencies have to installed
89+
# for the target platform: https://github.com/tonistiigi/xx#go--cgo
90+
RUN xx-apk add --no-cache \
91+
musl-dev \
92+
gcc
93+
94+
ENV CGO_ENABLED=1
95+
RUN export LIBRARY_PATH="/usr/local/$(xx-info triple)/lib:/usr/local/$(xx-info triple)/lib64:${LIBRARY_PATH}" && \
96+
export PKG_CONFIG_PATH="/usr/local/$(xx-info triple)/lib/pkgconfig:/usr/local/$(xx-info triple)/lib64/pkgconfig" && \
97+
export FLAGS="$(pkg-config --static --libs --cflags libssh2 openssl libgit2)" && \
98+
CGO_LDFLAGS="${FLAGS} -static" \
99+
xx-go build \
100+
-ldflags "-s -w" \
101+
-tags 'netgo,osusergo,static_build' \
102+
-o static-test-runner -trimpath main.go;
103+
104+
# Ensure that the generated binary is valid for the target platform
105+
RUN xx-verify --static static-test-runner
106+
107+
# This can be deployed into a gcr.io/distroless/static, however
108+
# the alpine has been chosen so it can run the static application
109+
# using the `RUN` statement.
110+
FROM ${BASE_VARIANT}
111+
112+
WORKDIR /root/smoketest
113+
COPY tests/smoketest/keys /root/smoketest/keys
114+
COPY --from=build \
115+
/root/smoketest/static-test-runner .
116+
117+
ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
118+
119+
# To do docker run instead, replace the RUN statement with:
120+
# ENTRYPOINT [ "/root/smoketest/static-test-runner" ]
121+
122+
# The approach below was preferred as it provides a way to
123+
# assert the functionality across the supported architectures
124+
# without any extra steps.
125+
126+
RUN /root/smoketest/static-test-runner

Makefile

+5-7
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ STATIC_TEST_TAG := test
55
PLATFORMS ?= linux/amd64,linux/arm/v7,linux/arm64
66
BUILD_ARGS ?=
77

8-
GIT2GO_TAG ?= v31.6.1
9-
108
.PHONY: build
119
build:
1210
docker buildx build \
@@ -20,18 +18,18 @@ test:
2018
docker buildx build \
2119
--platform=$(PLATFORMS) \
2220
--tag $(IMG):$(TAG) \
23-
--build-arg IMG=$(IMG) \
24-
--build-arg TAG=$(TAG) \
25-
--build-arg GIT2GO_TAG=$(GIT2GO_TAG) \
26-
--build-arg CACHE_BUST="$(shell date --rfc-3339=ns --utc)" \
27-
--file Dockerfile.test .
21+
--file Dockerfile.test \
22+
$(BUILD_ARGS) .
2823

2924
.PHONY: builder
3025
builder:
26+
# create local builder
3127
docker buildx create --name local-builder \
3228
--platform $(PLATFORMS) \
3329
--driver-opt network=host \
3430
--driver-opt env.BUILDKIT_STEP_LOG_MAX_SIZE=1073741274 \
3531
--driver-opt env.BUILDKIT_STEP_LOG_MAX_SPEED=5000000000000 \
3632
--buildkitd-flags '--allow-insecure-entitlement security.insecure' \
3733
--use
34+
# install qemu emulators
35+
docker run -it --rm --privileged tonistiigi/binfmt --install all

README.md

+44-77
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
# golang-with-libgit2
22

3-
This repository contains a `Dockerfile` which `Makefile` content can be used to build the [libgit2][] dependency
4-
chain for **AMD64, ARM64 and ARMv7** binaries of Go projects that depend on [git2go][].
3+
This repository contains a `Dockerfile` with two files: `Makefile` and `static.sh`.
4+
Both of which can be used to build the [libgit2][] dependency chain for **AMD64, ARM64 and ARMv7** binaries
5+
of Go projects that depend on [git2go][].
6+
7+
The `Makefile` is useful for development environments and will leverage OS specific packages to build `libgit2`.
8+
The `static.sh` will build all `libgit2` dependencies from source using `musl` toolchain. This enables for a full
9+
static binary with the freedom of configuring each of the dependencies in chain.
510

611
### :warning: **Public usage discouraged**
712

@@ -42,86 +47,20 @@ while testing these against the git2go code before releasing the image.
4247
- [ ] [libgit2/git2go#836](https://github.com/libgit2/git2go/issues/836)
4348
- [ ] [libgit2/git2go#837](https://github.com/libgit2/git2go/issues/837)
4449

45-
## Usage
46-
47-
To make use of the image published by the `Dockerfile`, copy over the `Makefile` from the image as a prerequisite to
48-
your Go build. Once copied over to the image, the set of targets can be used to compile `libgit2` for the
49-
`$BUILDPLATFORM` and/or `$TARGETPLATFORM` (see [example](#Dockerfile-example)).
50-
51-
### Runtime dependencies
52-
53-
The following dependencies should be present in the image running the application:
54-
55-
- `libc6`
56-
- `ca-certificates`
57-
- `zlib1g/bookworm`
58-
- `libssl1.1/bookworm`
59-
- `libssh2-1/bookworm`
60-
61-
**Note:** at present, all dependencies suffixed with `bookworm` must be installed from Debian's `bookworm` release,
62-
[due to a misconfiguration in `libssh2-1` in earlier versions][libssh2-1-misconfiguration].
63-
64-
### `Dockerfile` example
65-
66-
```Dockerfile
67-
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
68-
FROM fluxcd/golang-with-libgit2 as libgit2
69-
70-
FROM --platform=$BUILDPLATFORM golang:1.17.6-bullseye as build
71-
72-
# Copy the build utiltiies
73-
COPY --from=xx / /
74-
COPY --from=libgit2 /Makefile /libgit2/
75-
76-
# Install the libgit2 build dependencies
77-
RUN make -C /libgit2 cmake
78-
79-
ARG TARGETPLATFORM
80-
RUN make -C /libgit2 dependencies
81-
82-
# Compile and install libgit2
83-
RUN FLAGS=$(xx-clang --print-cmake-defines) make -C /libgit2 libgit2 \
84-
&& mkdir -p /libgit2/lib/ \
85-
&& cp -d /usr/lib/$(xx-info triple)/libgit2.so* /libgit2/lib/
50+
---
51+
**NOTE**
8652

87-
# Configure workspace
88-
WORKDIR /workspace
53+
The issues above do not affect libgit2 built with `static.sh` as all its
54+
dependencies have been configured to be optimal for its use, as the first supported version of libgit2 is `1.3.0`.
8955

90-
# Copy modules manifests
91-
COPY go.mod go.mod
92-
COPY go.sum go.sum
56+
---
9357

94-
# Cache modules
95-
RUN go mod download
96-
97-
# Copy source code
98-
COPY main.go main.go
99-
100-
# Build the binary
101-
ENV CGO_ENABLED=1
102-
ARG TARGETPLATFORM
103-
RUN xx-go build -o app \
104-
main.go
105-
106-
FROM debian:bookworm-slim as controller
107-
108-
# Install runtime dependencies
109-
RUN apt update \
110-
&& apt install -y zlib1g/bookworm libssl1.1 libssh2-1 \
111-
&& apt install -y ca-certificates \
112-
&& apt clean \
113-
&& apt autoremove --purge -y \
114-
&& rm -rf /var/lib/apt/lists/*
115-
116-
# Copy libgit2.so*
117-
COPY --from=build /libgit2/lib/ /usr/local/lib/
118-
RUN ldconfig
58+
## Usage
11959

120-
# Copy over binary from build
121-
COPY --from=build /workspace/app /usr/local/bin/
60+
The [Dockerfile.test](./Dockerfile.test) file provides a working example on how to statically build a golang application that has a dependency to libgit2 and git2go.
12261

123-
ENTRYPOINT [ "app" ]
124-
```
62+
The example will statically build all dependencies based on the versions specified on `static.sh`.
63+
Then statically build the golang application and deploy it into an image based off `gcr.io/distroless/static`.
12564

12665
## Contributing
12766

@@ -162,6 +101,34 @@ In case changes happen to the `Dockerfile` while the `libgit2` version does not
162101
be suffixed with `-<seq num in range>`. For example, `libgit2-1.1.1-2` for the **third** container image
163102
with the same version.
164103

104+
### Debugging cross-compilation
105+
106+
Below are a few tips on how to overcome cross-compilation issues:
107+
108+
1) Ensure all qemu emulators are installed:
109+
```sh
110+
docker run -it --rm --privileged tonistiigi/binfmt --install all
111+
```
112+
113+
2) Check that the generated libraries are aligned with the target architecture:
114+
115+
Leveraging `readelf` from `binutils` (i.e. `apk add binutils`), check the target machine
116+
architecture:
117+
118+
```sh
119+
$ readelf -h /usr/local/aarch64-alpine-linux-musl/lib/libcrypto.a | grep Machine |sort -u
120+
Machine: AArch64
121+
$ readelf -h /usr/local/aarch64-alpine-linux-musl/lib/libgit2.a | grep Machine | sort -u
122+
Machine: AArch64
123+
$ readelf -h /usr/local/aarch64-alpine-linux-musl/lib/libssh2.a | grep Machine | sort -u
124+
Machine: AArch64
125+
$ readelf -h /usr/local/aarch64-alpine-linux-musl/lib/libssl.a | grep Machine | sort -u
126+
Machine: AArch64
127+
$ readelf -h /usr/local/aarch64-alpine-linux-musl/lib/libz.a | grep Machine | sort -u
128+
Machine: AArch64
129+
```
130+
131+
165132
[xx]: https://github.com/tonistiigi/xx
166133
[Go container image]: https://hub.docker.com/_/golang
167134
[libgit2]: https://github.com/libgit2/libgit2

hack/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ CMAKE_VERSION ?= 3.21.3
2626
# Libgit2 version to be compiled and installed.
2727
LIBGIT2_VERSION ?= 1.1.1
2828
# In some scenarios libgit2 needs to be checked out to a specific commit.
29-
# This takes presidence over LIBGIT_VERSION if defined.
29+
# This takes precedence over LIBGIT_VERSION if defined.
3030
# Ref: https://github.com/libgit2/git2go/issues/834
3131
LIBGIT2_REVISION ?=
3232

0 commit comments

Comments
 (0)