From 3008a82281cbf7685a896c839ffeae6743b5260a Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Tue, 8 Feb 2022 17:27:30 +0000 Subject: [PATCH 01/11] Add smoke tests for development environment Signed-off-by: Paulo Gomes --- Dockerfile.test | 95 +++++---------------------------------- Makefile | 51 ++++++++++++++++++++- hack/extract-libraries.sh | 64 ++++++++++++++++++++++++++ hack/static.sh | 44 +++++++++++++++--- tests/smoketest/main.go | 44 +++++++++++------- 5 files changed, 189 insertions(+), 109 deletions(-) create mode 100755 hack/extract-libraries.sh diff --git a/Dockerfile.test b/Dockerfile.test index 870c658..1876c93 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,96 +1,28 @@ -# This Dockerfile tests the hack/Makefile output against git2go. ARG BASE_VARIANT=alpine -ARG GO_VERSION=1.17.6 -ARG XX_VERSION=1.1.0 -FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx +ARG LIBGIT2_IMG +ARG LIBGIT2_TAG -FROM golang:${GO_VERSION}-${BASE_VARIANT} as gostable +FROM --platform=linux/amd64 ${LIBGIT2_IMG}:${LIBGIT2_TAG} as build-amd64 +FROM --platform=linux/arm64 ${LIBGIT2_IMG}:${LIBGIT2_TAG} as build-arm64 +FROM --platform=linux/arm/v7 ${LIBGIT2_IMG}:${LIBGIT2_TAG} as build-armv7 -FROM gostable AS go-linux +FROM --platform=$BUILDPLATFORM build-$TARGETARCH$TARGETVARIANT AS build -FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-deps - -RUN apk add --no-cache \ - bash \ - curl \ - build-base \ - linux-headers \ - perl \ - cmake \ - pkgconfig \ - gcc \ - musl-dev \ - clang \ - lld - -COPY --from=xx / / +RUN apk add clang lld pkgconfig ca-certificates ARG TARGETPLATFORM +# Some dependencies have to installed +# for the target platform: https://github.com/tonistiigi/xx#go--cgo RUN xx-apk add --no-cache \ - xx-c-essentials - -RUN xx-apk add --no-cache \ - xx-cxx-essentials - -ARG TARGETPLATFORM -RUN xx-apk add --no-cache \ - build-base \ - pkgconfig \ - gcc \ - musl-dev \ - clang \ - lld \ - llvm \ - linux-headers - -WORKDIR /build -COPY hack/static.sh . - -ARG TARGETPLATFORM -ENV CC=xx-clang -ENV CXX=xx-clang++ - -RUN CHOST=$(xx-clang --print-target-triple) \ - ./static.sh build_libz - -RUN CHOST=$(xx-clang --print-target-triple) \ - ./static.sh build_openssl - -RUN export LIBRARY_PATH="/usr/local/$(xx-info triple)/lib:/usr/local/$(xx-info triple)/lib64:${LIBRARY_PATH}" && \ - export PKG_CONFIG_PATH="/usr/local/$(xx-info triple)/lib/pkgconfig:/usr/local/$(xx-info triple)/lib64/pkgconfig" && \ - export OPENSSL_ROOT_DIR="/usr/local/$(xx-info triple)" && \ - export OPENSSL_CRYPTO_LIBRARY="/usr/local/$(xx-info triple)/lib64" && \ - export OPENSSL_INCLUDE_DIR="/usr/local/$(xx-info triple)/include/openssl" - -RUN ./static.sh build_libssh2 -RUN ./static.sh build_libgit2 - - -FROM go-${TARGETOS} AS build - -# Copy cross-compilation tools -COPY --from=xx / / -# Copy compiled libraries -COPY --from=build-deps /usr/local/ /usr/local/ - -RUN apk add clang lld pkgconfig + musl-dev gcc lld binutils-gold WORKDIR /root/smoketest COPY tests/smoketest/go.mod . COPY tests/smoketest/go.sum . RUN go mod download -ARG TARGETPLATFORM - -# Some dependencies have to installed -# for the target platform: https://github.com/tonistiigi/xx#go--cgo -RUN xx-apk add --no-cache \ - musl-dev \ - gcc - - COPY tests/smoketest/main.go . ENV CGO_ENABLED=1 @@ -119,11 +51,4 @@ COPY --from=build \ ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -# To do docker run instead, replace the RUN statement with: -# ENTRYPOINT [ "/root/smoketest/static-test-runner" ] - -# The approach below was preferred as it provides a way to -# assert the functionality across the supported architectures -# without any extra steps. - RUN /root/smoketest/static-test-runner diff --git a/Makefile b/Makefile index c81266a..628c3b7 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,41 @@ -IMG ?= hiddeco/golang-with-libgit2 +IMG ?= ghcr.io/fluxcd/golang-with-libgit2 TAG ?= latest -STATIC_TEST_TAG := test PLATFORMS ?= linux/amd64,linux/arm/v7,linux/arm64 BUILD_ARGS ?= +REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel) +TARGET_DIR ?= $(REPOSITORY_ROOT)/build/libgit2 +BUILD_ROOT_DIR ?= $(REPOSITORY_ROOT)/build/libgit2-src + +LIBGIT2_PATH := $(TARGET_DIR) +LIBGIT2_LIB_PATH := $(LIBGIT2_PATH)/lib +LIBGIT2_LIB64_PATH := $(LIBGIT2_PATH)/lib64 +LIBGIT2 := $(LIBGIT2_LIB_PATH)/libgit2.a + +export CGO_ENABLED=1 +export LIBRARY_PATH=$(LIBGIT2_LIB_PATH):$(LIBGIT2_LIB64_PATH) +export PKG_CONFIG_PATH=$(LIBGIT2_LIB_PATH)/pkgconfig:$(LIBGIT2_LIB64_PATH)/pkgconfig +export CGO_CFLAGS=-I$(LIBGIT2_PATH)/include + + +ifeq ($(shell uname -s),Linux) + export CGO_LDFLAGS=$(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs --static --cflags libssh2 openssl libgit2) -static +else + export CGO_LDFLAGS=$(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs --static --cflags libssh2 openssl libgit2) -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-allow-shlib-undefined -static +endif + +ifeq ($(shell uname -s),Linux) +ifneq ($(shell uname -m),x86_64) + export CC=$(shell uname -m)-linux-musl-gcc + export CXX=$(shell uname -m)-linux-musl-g++ + export AR=$(shell uname -m)-linux-musl-ar +endif +endif + +GO_STATIC_FLAGS=-tags 'netgo,osusergo,static_build' + + .PHONY: build build: docker buildx build \ @@ -18,6 +49,8 @@ test: docker buildx build \ --platform=$(PLATFORMS) \ --tag $(IMG):$(TAG) \ + --build-arg LIBGIT2_IMG=$(IMG) \ + --build-arg LIBGIT2_TAG=$(TAG) \ --file Dockerfile.test \ $(BUILD_ARGS) . @@ -33,3 +66,17 @@ builder: --use # install qemu emulators docker run -it --rm --privileged tonistiigi/binfmt --install all + +$(LIBGIT2): +ifeq ($(shell uname -s),Darwin) + TARGET_DIR=$(TARGET_DIR) BUILD_ROOT_DIR=$(BUILD_ROOT_DIR) \ + ./hack/static.sh all +else + IMG_TAG=$(IMG):$(TAG) ./hack/extract-libraries.sh +endif + +# dev-test is a smoke test for development environment +# consuming the libraries generated by this project. +dev-test: $(LIBGIT2) + cd tests/smoketest; go vet $(GO_STATIC_FLAGS) ./... + cd tests/smoketest; go run $(GO_STATIC_FLAGS) main.go diff --git a/hack/extract-libraries.sh b/hack/extract-libraries.sh new file mode 100755 index 0000000..8330223 --- /dev/null +++ b/hack/extract-libraries.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +IMG_TAG="${IMG_TAG:-.}" + +function extract(){ + PLATFORM=$1 + DIR=$2 + + id=$(docker create --platform="${PLATFORM}" "${IMG_TAG}") + docker cp "${id}":/usr/local - > output.tar.gz + docker rm -v "${id}" + + tar -xf output.tar.gz "local/${DIR}" + rm output.tar.gz +} + +function setup() { + PLATFORM=$1 + DIR=$2 + + extract "${PLATFORM}" "${DIR}" + + NEW_DIR="$(/bin/pwd)/build/libgit2" + INSTALLED_DIR="/usr/local/${DIR}" + + mkdir -p "./build" + + mv "local/${DIR}/" "libgit2" + rm -rf "local" + mv "libgit2/" "./build/" + + # Update the prefix paths included in the .pc files. + # This will make it easier to update to the location in which they will be used. + if [[ $OSTYPE == 'darwin'* ]]; then + # sed has a sight different behaviour in MacOS + find "${NEW_DIR}" -type f -name "*.pc" | xargs -I {} sed -i "" "s;${INSTALLED_DIR};${NEW_DIR};g" {} + else + find "${NEW_DIR}" -type f -name "*.pc" | xargs -I {} sed -i "s;${INSTALLED_DIR};${NEW_DIR};g" {} + fi +} + +function setup_current() { + if [ -d "./build/libgit2" ]; then + echo "Skipping libgit2 setup as it already exists" + exit 0 + fi + + DIR="x86_64-alpine-linux-musl" + PLATFORM="linux/amd64" + + if [[ "$(uname -m)" == armv7* ]]; then + DIR="armv7-alpine-linux-musleabihf" + PLATFORM="linux/arm/v7" + elif [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then + DIR="aarch64-alpine-linux-musl" + PLATFORM="linux/arm64" + fi + + setup "${PLATFORM}" "${DIR}" +} + +setup_current diff --git a/hack/static.sh b/hack/static.sh index 4fb7294..1611de1 100755 --- a/hack/static.sh +++ b/hack/static.sh @@ -14,13 +14,12 @@ TARGET_DIR="${TARGET_DIR:-/usr/local/$(xx-info triple)}" BUILD_ROOT_DIR="${BUILD_ROOT_DIR:-/build}" SRC_DIR="${BUILD_ROOT_DIR}/src" - TARGET_ARCH="$(uname -m)" if command -v xx-info; then TARGET_ARCH="$(xx-info march)" fi -C_COMPILER="/usr/bin/gcc" +C_COMPILER="${CC:-/usr/bin/gcc}" CMAKE_PARAMS="" if command -v xx-clang; then C_COMPILER="/usr/bin/xx-clang" @@ -40,6 +39,7 @@ function build_libz(){ download_source "${LIBZ_URL}" "${SRC_DIR}/libz" pushd "${SRC_DIR}/libz" + # if target architecture is the same as current, no cross compiling is required if [ "${TARGET_ARCH}" = "$(uname -m)" ]; then ./configure --static --prefix="${TARGET_DIR}" else @@ -56,14 +56,24 @@ function build_openssl(){ download_source "${OPENSSL_URL}" "${SRC_DIR}/openssl" pushd "${SRC_DIR}/openssl" - target_name="${TARGET_ARCH}" - if [ "${target_name}" = "armv7l" ]; then + export OPENSSL_ROOT_DIR="${TARGET_DIR}" + export OPENSSL_LIBRARIES="${TARGET_DIR}/lib" + + target_arch="" + if [ "${TARGET_ARCH}" = "armv7l" ]; then # openssl does not have a specific armv7l # using generic32 instead. - target_name=generic32 + target_arch="linux-generic32" + elif [ "${TARGET_ARCH}" = "arm64" ] || [ "${TARGET_ARCH}" = "aarch64" ]; then + target_arch="linux-aarch64" + elif [ "${TARGET_ARCH}" = "x86_64" ]; then + target_arch="linux-x86_64" + else + echo "Architecture currently not supported: ${TARGET_ARCH}" + exit 1 fi - ./Configure "linux-${target_name}" threads no-shared zlib -fPIC -DOPENSSL_PIC \ + ./Configure "${target_arch}" threads no-shared zlib -fPIC -DOPENSSL_PIC \ --prefix="${TARGET_DIR}" \ --with-zlib-include="${TARGET_DIR}/include" \ --with-zlib-lib="${TARGET_DIR}/lib" \ @@ -83,14 +93,23 @@ function build_libssh2(){ mkdir -p build pushd build + OPENSSL_LIBRARIES="${TARGET_DIR}/lib" + if [ "${TARGET_ARCH}" = "x86_64" ]; then + OPENSSL_LIBRARIES="${TARGET_DIR}/lib64" + fi + + cmake "${CMAKE_PARAMS}" \ -DCMAKE_C_COMPILER="${C_COMPILER}" \ -DCMAKE_INSTALL_PREFIX="${TARGET_DIR}" \ -DBUILD_SHARED_LIBS=OFF \ + -DLINT=OFF \ -DCMAKE_C_FLAGS=-fPIC \ -DCRYPTO_BACKEND=OpenSSL \ -DENABLE_ZLIB_COMPRESSION=ON \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ + -DOPENSSL_CRYPTO_LIBRARY="${OPENSSL_LIBRARIES}/libcrypto.a" \ + -DOPENSSL_SSL_LIBRARY="${OPENSSL_LIBRARIES}/libssl.a" \ .. cmake --build . --target install @@ -108,11 +127,19 @@ function build_libgit2(){ pushd build + SSL_LIBRARY="${TARGET_DIR}/lib/libssl.a" + CRYPTO_LIBRARY="${TARGET_DIR}/lib/libcrypto.a" + if [ "${TARGET_ARCH}" = "x86_64" ]; then + SSL_LIBRARY="${TARGET_DIR}/lib64/libssl.a" + CRYPTO_LIBRARY="${TARGET_DIR}/lib64/libcrypto.a" + fi + cmake "${CMAKE_PARAMS}" \ -DCMAKE_C_COMPILER="${C_COMPILER}" \ -DCMAKE_INSTALL_PREFIX="${TARGET_DIR}" \ -DTHREADSAFE:BOOL=ON \ - -DBUILD_CLAR:BOOL:BOOL=OFF \ + -DBUILD_CLAR:BOOL=OFF \ + -DBUILD_TESTS:BOOL=OFF \ -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON \ -DCMAKE_C_FLAGS=-fPIC \ @@ -122,6 +149,9 @@ function build_libgit2(){ -DUSE_BUNDLED_ZLIB:BOOL=ON \ -DUSE_HTTPS:STRING=OpenSSL \ -DREGEX_BACKEND:STRING=builtin \ + -DOPENSSL_SSL_LIBRARY="${SSL_LIBRARY}" \ + -DOPENSSL_CRYPTO_LIBRARY="${CRYPTO_LIBRARY}" \ + -DZLIB_LIBRARY="${TARGET_DIR}/lib/libz.a" \ -DCMAKE_INCLUDE_PATH="${TARGET_DIR}/include" \ -DCMAKE_LIBRARY_PATH="${TARGET_DIR}/lib" \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ diff --git a/tests/smoketest/main.go b/tests/smoketest/main.go index 5cde54b..5e54744 100644 --- a/tests/smoketest/main.go +++ b/tests/smoketest/main.go @@ -25,12 +25,14 @@ import ( "golang.org/x/crypto/ssh/knownhosts" ) -const testsDir = "/root/tests" - func main() { fmt.Println("Running tests...") + testsDir, err := filepath.Abs("./build/tests") + if err != nil { + panic(fmt.Errorf("filepath abs: %w", err)) + } os.MkdirAll(testsDir, 0o755) - defer os.RemoveAll(testsDir) + defer os.RemoveAll("./build") repoPath := "test.git" server := createTestServer(repoPath) @@ -61,6 +63,7 @@ func main() { if err != nil { panic(fmt.Errorf("scan host key: %w", err)) } + fmt.Printf("known_host entry: \n%s\n", knownHosts) sshRepoURL := fmt.Sprintf("%s/%s", server.SSHAddress(), repoPath) @@ -120,11 +123,11 @@ func createTestServer(repoPath string) *gittestserver.GitServer { server.AutoCreate() server.KeyDir(filepath.Join(server.Root(), "keys")) - os.MkdirAll("testdata/git/repo", 0o755) - os.WriteFile("testdata/git/repo/test123", []byte("test..."), 0o644) - os.WriteFile("testdata/git/repo/test321", []byte("test2..."), 0o644) + os.MkdirAll("build/testdata/git/repo", 0o755) + os.WriteFile("build/testdata/git/repo/test123", []byte("test..."), 0o644) + os.WriteFile("build/testdata/git/repo/test321", []byte("test2..."), 0o644) - if err = server.InitRepo("testdata/git/repo", git.DefaultBranch, repoPath); err != nil { + if err = server.InitRepo("build/testdata/git/repo", git.DefaultBranch, repoPath); err != nil { panic(fmt.Errorf("InitRepo: %w", err)) } return server @@ -151,6 +154,7 @@ func test(description, targetDir, repoURI string, cloneOptions *git2go.CloneOpti // git.SSH Transports. func knownHostsCallback(host string, knownHosts []byte) git2go.CertificateCheckCallback { return func(cert *git2go.Certificate, valid bool, hostname string) error { + fmt.Printf("[knownHostsCallback] valid: %v hostname: %q\n", valid, hostname) if cert == nil { return fmt.Errorf("no certificate returned for %s", hostname) } @@ -164,24 +168,34 @@ func knownHostsCallback(host string, knownHosts []byte) git2go.CertificateCheckC // First, attempt to split the configured host and port to validate // the port-less hostname given to the callback. - h, _, err := net.SplitHostPort(host) + hostWithoutPort, _, err := net.SplitHostPort(host) if err != nil { // SplitHostPort returns an error if the host is missing // a port, assume the host has no port. - h = host + hostWithoutPort = host + } + + // Different versions of libgit handle this differently. + // This fixes the case in which ports may be sent back. + hostnameWithoutPort, _, err := net.SplitHostPort(hostname) + if err != nil { + hostnameWithoutPort = hostname + + fmt.Printf("host and hostname:\n%q\n%q\n", + hostWithoutPort, + hostnameWithoutPort) } - // Check if the configured host matches the hostname given to - // the callback. - if h != hostname { - return fmt.Errorf("host mismatch: %q %q\n", h, hostname) + if hostnameWithoutPort != hostWithoutPort { + return fmt.Errorf("host mismatch: %q %q", hostnameWithoutPort, hostWithoutPort) } // We are now certain that the configured host and the hostname // given to the callback match. Use the configured host (that // includes the port), and normalize it, so we can check if there // is an entry for the hostname _and_ port. - h = knownhosts.Normalize(host) + h := knownhosts.Normalize(host) + fmt.Printf("normalised host (with port): %q\n", h) for _, k := range kh { if k.matches(h, cert.Hostkey) { return nil @@ -226,7 +240,7 @@ func parseKnownHosts(s string) ([]knownKey, error) { func (k knownKey) matches(host string, hostkey git2go.HostkeyCertificate) bool { if !containsHost(k.hosts, host) { - fmt.Println("HOST NOT FOUND") + fmt.Printf("host not found: %q\n", host) return false } From 215096c778d289f94e532786147fe67bbca8a11e Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Sun, 30 Jan 2022 20:46:19 +0000 Subject: [PATCH 02/11] Optimise image Signed-off-by: Paulo Gomes --- Dockerfile | 43 ++++++++++++++++++++++-------------------- Dockerfile.test | 50 ++++++++++++++++++++++++++++++++++--------------- Makefile | 2 +- 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index e30cae8..11bd33b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,11 +5,7 @@ ARG XX_VERSION=1.1.0 FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx -FROM golang:${GO_VERSION}-${BASE_VARIANT} as gostable - -FROM gostable AS go-linux - -FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-deps +FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-base RUN apk add --no-cache \ bash \ @@ -26,15 +22,10 @@ RUN apk add --no-cache \ COPY --from=xx / / -ARG TARGETPLATFORM - -RUN xx-apk add --no-cache \ - xx-c-essentials - -RUN xx-apk add --no-cache \ - xx-cxx-essentials +FROM build-base AS build-cross ARG TARGETPLATFORM + RUN xx-apk add --no-cache \ build-base \ pkgconfig \ @@ -48,7 +39,6 @@ RUN xx-apk add --no-cache \ WORKDIR /build COPY hack/static.sh . -ARG TARGETPLATFORM ENV CC=xx-clang ENV CXX=xx-clang++ @@ -68,12 +58,25 @@ RUN ./static.sh build_libssh2 RUN ./static.sh build_libgit2 -FROM go-${TARGETOS} AS build +# trimmed removes all non necessary files (i.e. openssl binary). +FROM build-cross AS trimmed -# Copy cross-compilation tools -COPY --from=xx / / -# Copy compiled libraries -COPY --from=build-deps /usr/local/ /usr/local/ +ARG TARGETPLATFORM +RUN mkdir -p /trimmed/usr/local/$(xx-info triple)/ && \ + mkdir -p /trimmed/usr/local/$(xx-info triple)/share + +RUN cp -r /usr/local/$(xx-info triple)/lib/ /trimmed/usr/local/$(xx-info triple)/ && \ + cp -r /usr/local/$(xx-info triple)/lib64/ /trimmed/usr/local/$(xx-info triple)/ | true && \ + cp -r /usr/local/$(xx-info triple)/include/ /trimmed/usr/local/$(xx-info triple)/ && \ + cp -r /usr/local/$(xx-info triple)/share/doc/ /trimmed/usr/local/$(xx-info triple)/share/ + +FROM scratch as libs-arm64 +COPY --from=trimmed /trimmed/ / + +FROM scratch as libs-amd64 +COPY --from=trimmed /trimmed/ / + +FROM scratch as libs-armv7 +COPY --from=trimmed /trimmed/ / -COPY ./hack/Makefile /Makefile -COPY ./hack/static.sh /static.sh +FROM libs-$TARGETARCH$TARGETVARIANT as libs diff --git a/Dockerfile.test b/Dockerfile.test index 1876c93..0ddeb6f 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,39 +1,59 @@ ARG BASE_VARIANT=alpine +ARG GO_VERSION=1.17.6 +ARG XX_VERSION=1.1.0 ARG LIBGIT2_IMG ARG LIBGIT2_TAG -FROM --platform=linux/amd64 ${LIBGIT2_IMG}:${LIBGIT2_TAG} as build-amd64 -FROM --platform=linux/arm64 ${LIBGIT2_IMG}:${LIBGIT2_TAG} as build-arm64 -FROM --platform=linux/arm/v7 ${LIBGIT2_IMG}:${LIBGIT2_TAG} as build-armv7 +FROM ${LIBGIT2_IMG}:${LIBGIT2_TAG} AS build-deps -FROM --platform=$BUILDPLATFORM build-$TARGETARCH$TARGETVARIANT AS build +FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx -RUN apk add clang lld pkgconfig ca-certificates +FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} as gostable -ARG TARGETPLATFORM +FROM gostable AS go-linux -# Some dependencies have to installed -# for the target platform: https://github.com/tonistiigi/xx#go--cgo -RUN xx-apk add --no-cache \ - musl-dev gcc lld binutils-gold +# Build-base consists of build platform dependencies and xx. +# These will be used at current arch to yield execute the cross compilations. +FROM go-${TARGETOS} AS build-base + +RUN apk add clang lld pkgconfig + +COPY --from=xx / / + +# build-go-mod can still be cached at build platform architecture. +FROM build-base as build-go-mod WORKDIR /root/smoketest COPY tests/smoketest/go.mod . COPY tests/smoketest/go.sum . RUN go mod download +# Build stage install per target platform +# dependency and effectively cross compile the application. +FROM build-go-mod as build + +ARG TARGETPLATFORM + +# Some dependencies have to installed +# for the target platform: https://github.com/tonistiigi/xx#go--cgo +RUN xx-apk add musl-dev gcc clang lld + +WORKDIR /root/smoketest + COPY tests/smoketest/main.go . +COPY --from=build-deps /usr/local/ /usr/local/ ENV CGO_ENABLED=1 -RUN export LIBRARY_PATH="/usr/local/$(xx-info triple)/lib:/usr/local/$(xx-info triple)/lib64:${LIBRARY_PATH}" && \ +RUN export LIBRARY_PATH="/usr/local/$(xx-info triple):/usr/local/$(xx-info triple)/lib64" && \ export PKG_CONFIG_PATH="/usr/local/$(xx-info triple)/lib/pkgconfig:/usr/local/$(xx-info triple)/lib64/pkgconfig" && \ - export FLAGS="$(pkg-config --static --libs --cflags libssh2 openssl libgit2)" && \ - CGO_LDFLAGS="${FLAGS} -static" \ - xx-go build \ + export FLAGS="$(pkg-config --static --libs --cflags libssh2 openssl libgit2)" && \ + export CGO_LDFLAGS="${FLAGS} -static" && \ + xx-go build \ -ldflags "-s -w" \ -tags 'netgo,osusergo,static_build' \ - -o static-test-runner -trimpath main.go; + -o static-test-runner -trimpath main.go + # Ensure that the generated binary is valid for the target platform RUN xx-verify --static static-test-runner diff --git a/Makefile b/Makefile index 628c3b7..517b754 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ build: test: docker buildx build \ --platform=$(PLATFORMS) \ - --tag $(IMG):$(TAG) \ + --tag $(IMG):$(TAG)-test \ --build-arg LIBGIT2_IMG=$(IMG) \ --build-arg LIBGIT2_TAG=$(TAG) \ --file Dockerfile.test \ From 06d7d12510e3a067f3255b416114bdee272798d7 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Mon, 31 Jan 2022 10:12:15 +0000 Subject: [PATCH 03/11] Unpin GO minor version Aligns with what is being used for IAC and SC. Signed-off-by: Paulo Gomes --- Dockerfile.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.test b/Dockerfile.test index 0ddeb6f..8c92d87 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,5 +1,5 @@ ARG BASE_VARIANT=alpine -ARG GO_VERSION=1.17.6 +ARG GO_VERSION=1.17 ARG XX_VERSION=1.1.0 ARG LIBGIT2_IMG From e759482fa86e23dc16d04651e8f301ef5ee4696a Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Mon, 31 Jan 2022 10:12:30 +0000 Subject: [PATCH 04/11] Add musl tool chain for linux arm64 Signed-off-by: Paulo Gomes --- Makefile | 14 ++++++++++---- hack/download-musl.sh | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100755 hack/download-musl.sh diff --git a/Makefile b/Makefile index 517b754..827e5ff 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ LIBGIT2_PATH := $(TARGET_DIR) LIBGIT2_LIB_PATH := $(LIBGIT2_PATH)/lib LIBGIT2_LIB64_PATH := $(LIBGIT2_PATH)/lib64 LIBGIT2 := $(LIBGIT2_LIB_PATH)/libgit2.a +MUSL-CC = export CGO_ENABLED=1 export LIBRARY_PATH=$(LIBGIT2_LIB_PATH):$(LIBGIT2_LIB64_PATH) @@ -27,9 +28,11 @@ endif ifeq ($(shell uname -s),Linux) ifneq ($(shell uname -m),x86_64) - export CC=$(shell uname -m)-linux-musl-gcc - export CXX=$(shell uname -m)-linux-musl-g++ - export AR=$(shell uname -m)-linux-musl-ar + MUSL-PREFIX=$(REPOSITORY_ROOT)/build/musl/aarch64-linux-musl-native/bin/aarch64-linux-musl + MUSL-CC=$(MUSL-PREFIX)-gcc + export CC=$(MUSL-PREFIX)-gcc + export CXX=$(MUSL-PREFIX)-g++ + export AR=$(MUSL-PREFIX)-ar endif endif @@ -67,7 +70,10 @@ builder: # install qemu emulators docker run -it --rm --privileged tonistiigi/binfmt --install all -$(LIBGIT2): +$(MUSL-CC): + ./hack/download-musl.sh + +$(LIBGIT2): $(MUSL-CC) ifeq ($(shell uname -s),Darwin) TARGET_DIR=$(TARGET_DIR) BUILD_ROOT_DIR=$(BUILD_ROOT_DIR) \ ./hack/static.sh all diff --git a/hack/download-musl.sh b/hack/download-musl.sh new file mode 100755 index 0000000..ff774a1 --- /dev/null +++ b/hack/download-musl.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +MUSL_AARCH64_FILENAME=aarch64-linux-musl-native.tgz +MUSL_AARCH64_URL="https://more.musl.cc/11.2.1/x86_64-linux-musl/${MUSL_AARCH64_FILENAME}" +MUSL_AARCH64_SHA512=16d544e09845c9dbba50f29e0cb04dd661e17eb63c56acad6a67fd2a78aa7596b792477c7177d3cd56d408a27dc291a90507df882f2b099c0f25511ce08fd3b5 + +ROOT_DIR="$(git rev-parse --show-toplevel)" +MUSL_DIR="${ROOT_DIR}/build/musl" + +if [ ! -f "${MUSL_DIR}/bin" ]; then + TARGET_FILE="${MUSL_DIR}/${MUSL_AARCH64_FILENAME}" + mkdir -p "${MUSL_DIR}" + + echo "${MUSL_AARCH64_SHA512} ${TARGET_FILE}" + curl -o "${TARGET_FILE}" -LO "${MUSL_AARCH64_URL}" + if ! echo "${MUSL_AARCH64_SHA512} ${TARGET_FILE}" | sha512sum --check; then + echo "Checksum failed for ${MUSL_AARCH64_FILENAME}." + rm -rf "${MUSL_DIR}" + exit 1 + fi + + tar xzf "${TARGET_FILE}" -C "${MUSL_DIR}" + rm "${TARGET_FILE}" +fi From 74493ec438dee32e5f73bd33dfb9e63644b3969f Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Mon, 31 Jan 2022 14:19:09 +0000 Subject: [PATCH 05/11] Add native x86_64 musl toolchain Allows local development without dependending on musl being installed on the development machine. Signed-off-by: Paulo Gomes --- Makefile | 12 +++++++----- hack/download-musl.sh | 22 +++++++++++++++++----- hack/extract-libraries.sh | 4 ++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 827e5ff..d99ede8 100644 --- a/Makefile +++ b/Makefile @@ -27,14 +27,12 @@ else endif ifeq ($(shell uname -s),Linux) -ifneq ($(shell uname -m),x86_64) - MUSL-PREFIX=$(REPOSITORY_ROOT)/build/musl/aarch64-linux-musl-native/bin/aarch64-linux-musl + MUSL-PREFIX=$(REPOSITORY_ROOT)/build/musl/$(shell uname -m)-linux-musl-native/bin/$(shell uname -m)-linux-musl MUSL-CC=$(MUSL-PREFIX)-gcc export CC=$(MUSL-PREFIX)-gcc export CXX=$(MUSL-PREFIX)-g++ export AR=$(MUSL-PREFIX)-ar endif -endif GO_STATIC_FLAGS=-tags 'netgo,osusergo,static_build' @@ -70,8 +68,6 @@ builder: # install qemu emulators docker run -it --rm --privileged tonistiigi/binfmt --install all -$(MUSL-CC): - ./hack/download-musl.sh $(LIBGIT2): $(MUSL-CC) ifeq ($(shell uname -s),Darwin) @@ -81,6 +77,12 @@ else IMG_TAG=$(IMG):$(TAG) ./hack/extract-libraries.sh endif +$(MUSL-CC): +ifneq ($(shell uname -s),Darwin) + ./hack/download-musl.sh +endif + + # dev-test is a smoke test for development environment # consuming the libraries generated by this project. dev-test: $(LIBGIT2) diff --git a/hack/download-musl.sh b/hack/download-musl.sh index ff774a1..de6b4a3 100755 --- a/hack/download-musl.sh +++ b/hack/download-musl.sh @@ -1,20 +1,32 @@ #!/usr/bin/env bash +set -eoux pipefail + +MUSL_X86_64_FILENAME=x86_64-linux-musl-native.tgz +MUSL_X86_64_SHA512=44d441ad9aa11a06feddf3daa4c9f53ad7d9ca37af1f5a61379aca07793703d179410cea723c1b7fca94c4de19a321228bdb3656bc5cbdb5e3bea8e2d6dac6c7 MUSL_AARCH64_FILENAME=aarch64-linux-musl-native.tgz -MUSL_AARCH64_URL="https://more.musl.cc/11.2.1/x86_64-linux-musl/${MUSL_AARCH64_FILENAME}" MUSL_AARCH64_SHA512=16d544e09845c9dbba50f29e0cb04dd661e17eb63c56acad6a67fd2a78aa7596b792477c7177d3cd56d408a27dc291a90507df882f2b099c0f25511ce08fd3b5 +MUSL_FILENAME="${MUSL_X86_64_FILENAME}" +MUSL_SHA512="${MUSL_X86_64_SHA512}" +if [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then + MUSL_FILENAME="${MUSL_AARCH64_FILENAME}" + MUSL_SHA512="${MUSL_AARCH64_SHA512}" +fi + +MUSL_AARCH64_URL="https://more.musl.cc/11.2.1/x86_64-linux-musl/${MUSL_FILENAME}" + ROOT_DIR="$(git rev-parse --show-toplevel)" MUSL_DIR="${ROOT_DIR}/build/musl" if [ ! -f "${MUSL_DIR}/bin" ]; then - TARGET_FILE="${MUSL_DIR}/${MUSL_AARCH64_FILENAME}" + TARGET_FILE="${MUSL_DIR}/${MUSL_FILENAME}" mkdir -p "${MUSL_DIR}" - echo "${MUSL_AARCH64_SHA512} ${TARGET_FILE}" + echo "${MUSL_SHA512} ${TARGET_FILE}" curl -o "${TARGET_FILE}" -LO "${MUSL_AARCH64_URL}" - if ! echo "${MUSL_AARCH64_SHA512} ${TARGET_FILE}" | sha512sum --check; then - echo "Checksum failed for ${MUSL_AARCH64_FILENAME}." + if ! echo "${MUSL_SHA512} ${TARGET_FILE}" | sha512sum --check; then + echo "Checksum failed for ${MUSL_FILENAME}." rm -rf "${MUSL_DIR}" exit 1 fi diff --git a/hack/extract-libraries.sh b/hack/extract-libraries.sh index 8330223..8458847 100755 --- a/hack/extract-libraries.sh +++ b/hack/extract-libraries.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -euxo pipefail +set -eoux pipefail IMG_TAG="${IMG_TAG:-.}" @@ -8,7 +8,7 @@ function extract(){ PLATFORM=$1 DIR=$2 - id=$(docker create --platform="${PLATFORM}" "${IMG_TAG}") + id=$(docker create --platform="${PLATFORM}" "${IMG_TAG}" sh) docker cp "${id}":/usr/local - > output.tar.gz docker rm -v "${id}" From 6dbaf6bb00389d0c007b5a07c1e9bf6e08ff61e9 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Tue, 8 Feb 2022 17:28:23 +0000 Subject: [PATCH 06/11] Run tests using key in memory Signed-off-by: Paulo Gomes --- Dockerfile | 2 +- Dockerfile.test | 88 +++++++++++++++++++++++++++++++++++++---- tests/smoketest/main.go | 6 +-- 3 files changed, 84 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 11bd33b..82304d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # This Dockerfile tests the hack/Makefile output against git2go. ARG BASE_VARIANT=alpine -ARG GO_VERSION=1.17.6 +ARG GO_VERSION=1.17 ARG XX_VERSION=1.1.0 FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx diff --git a/Dockerfile.test b/Dockerfile.test index 8c92d87..29e862d 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,13 +1,87 @@ +# This Dockerfile tests the hack/Makefile output against git2go. ARG BASE_VARIANT=alpine ARG GO_VERSION=1.17 ARG XX_VERSION=1.1.0 -ARG LIBGIT2_IMG -ARG LIBGIT2_TAG +FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx -FROM ${LIBGIT2_IMG}:${LIBGIT2_TAG} AS build-deps +FROM --platform=$BUILDPLATFORM ${BASE_VARIANT} AS build-base + +RUN apk add --no-cache \ + bash \ + curl \ + build-base \ + linux-headers \ + perl \ + cmake \ + pkgconfig \ + gcc \ + musl-dev \ + clang \ + lld -FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx +COPY --from=xx / / + +FROM build-base AS build-cross + +ARG TARGETPLATFORM + +RUN xx-apk add --no-cache \ + build-base \ + pkgconfig \ + gcc \ + musl-dev \ + clang \ + lld \ + llvm \ + linux-headers + +WORKDIR /build +COPY hack/static.sh . + +ENV CC=xx-clang +ENV CXX=xx-clang++ + +RUN CHOST=$(xx-clang --print-target-triple) \ + ./static.sh build_libz + +RUN CHOST=$(xx-clang --print-target-triple) \ + ./static.sh build_openssl + +RUN export LIBRARY_PATH="/usr/local/$(xx-info triple)/lib:/usr/local/$(xx-info triple)/lib64:${LIBRARY_PATH}" && \ + export PKG_CONFIG_PATH="/usr/local/$(xx-info triple)/lib/pkgconfig:/usr/local/$(xx-info triple)/lib64/pkgconfig" && \ + export OPENSSL_ROOT_DIR="/usr/local/$(xx-info triple)" && \ + export OPENSSL_CRYPTO_LIBRARY="/usr/local/$(xx-info triple)/lib64" && \ + export OPENSSL_INCLUDE_DIR="/usr/local/$(xx-info triple)/include/openssl" + +RUN ./static.sh build_libssh2 +RUN ./static.sh build_libgit2 + + +# trimmed removes all non necessary files (i.e. openssl binary). +FROM build-cross AS trimmed + +ARG TARGETPLATFORM +RUN mkdir -p /trimmed/usr/local/$(xx-info triple)/ && \ + mkdir -p /trimmed/usr/local/$(xx-info triple)/share + +RUN cp -r /usr/local/$(xx-info triple)/lib/ /trimmed/usr/local/$(xx-info triple)/ && \ + cp -r /usr/local/$(xx-info triple)/lib64/ /trimmed/usr/local/$(xx-info triple)/ | true && \ + cp -r /usr/local/$(xx-info triple)/include/ /trimmed/usr/local/$(xx-info triple)/ && \ + cp -r /usr/local/$(xx-info triple)/share/doc/ /trimmed/usr/local/$(xx-info triple)/share/ + +FROM scratch as libs-arm64 +COPY --from=trimmed /trimmed/ / + +FROM scratch as libs-amd64 +COPY --from=trimmed /trimmed/ / + +FROM scratch as libs-armv7 +COPY --from=trimmed /trimmed/ / + +FROM libs-$TARGETARCH$TARGETVARIANT as libs + +# Everything above this line is a copy from Dockefile. FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} as gostable @@ -15,14 +89,14 @@ FROM gostable AS go-linux # Build-base consists of build platform dependencies and xx. # These will be used at current arch to yield execute the cross compilations. -FROM go-${TARGETOS} AS build-base +FROM go-${TARGETOS} AS go-base RUN apk add clang lld pkgconfig COPY --from=xx / / # build-go-mod can still be cached at build platform architecture. -FROM build-base as build-go-mod +FROM go-base as build-go-mod WORKDIR /root/smoketest COPY tests/smoketest/go.mod . @@ -42,7 +116,7 @@ RUN xx-apk add musl-dev gcc clang lld WORKDIR /root/smoketest COPY tests/smoketest/main.go . -COPY --from=build-deps /usr/local/ /usr/local/ +COPY --from=libs /usr/local/ /usr/local/ ENV CGO_ENABLED=1 RUN export LIBRARY_PATH="/usr/local/$(xx-info triple):/usr/local/$(xx-info triple)/lib64" && \ diff --git a/tests/smoketest/main.go b/tests/smoketest/main.go index 5e54744..b69389f 100644 --- a/tests/smoketest/main.go +++ b/tests/smoketest/main.go @@ -80,8 +80,7 @@ func main() { FetchOptions: git2go.FetchOptions{ RemoteCallbacks: git2go.RemoteCallbacks{ CredentialsCallback: func(url string, username string, allowedTypes git2go.CredentialType) (*git2go.Credential, error) { - return git2go.NewCredentialSSHKeyFromMemory("git", - string(rsa.PublicKey), string(rsa.PrivateKey), "") + return git2go.NewCredentialSSHKeyFromMemory("git", string(rsa.PublicKey), string(rsa.PrivateKey), "") }, CertificateCheckCallback: knownHostsCallback(u.Host, knownHosts), }, @@ -100,8 +99,7 @@ func main() { FetchOptions: git2go.FetchOptions{ RemoteCallbacks: git2go.RemoteCallbacks{ CredentialsCallback: func(url string, username string, allowedTypes git2go.CredentialType) (*git2go.Credential, error) { - return git2go.NewCredentialSSHKeyFromMemory("git", - string(ed25519.PublicKey), string(ed25519.PrivateKey), "") + return git2go.NewCredentialSSHKeyFromMemory("git", string(ed25519.PublicKey), string(ed25519.PrivateKey), "") }, CertificateCheckCallback: knownHostsCallback(u.Host, knownHosts), }, From 13b2ad0bcc082c7f87f6b19c8606cac5fd578195 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Mon, 7 Feb 2022 12:39:52 +0000 Subject: [PATCH 07/11] Release darwin universal static binaries The changes on static.sh highlight the nuances of compiling for linux and mac, as well as cross compiling on the latter. Signed-off-by: Paulo Gomes --- .github/workflows/build.yaml | 84 +++++++++++++++++++++++++++++++++++- README.md | 13 +++--- hack/static.sh | 63 +++++++++++++++++++-------- 3 files changed, 134 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c2b7d63..e2643aa 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -11,7 +11,89 @@ on: pull_request: paths-ignore: - README.md + +permissions: + contents: write # needed to write releases + packages: write # needed for ghcr access + jobs: + + mac-build: + # This job builds and releases "universal libraries" that are + # supported by both darwin-amd64 and darwin-arm64. + # + # First builds in amd64, then cross-compile in arm64. Later combining + # both outcomes onto a single binary for each static library. + # + # `macos-11` has been picked as support for arm64 was only added on Xcode 12. + # Although some minor versions of Catalina 10.15 can support it, at the time + # of testing, GitHub's macos-10.15 did not seem to. + # Cross-compiling to arm64 on that runner consistently failed. + runs-on: macos-11 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Build universal static libraries for Darwin + run: | + TARGET_DIR=${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64 \ + BUILD_ROOT_DIR=${GITHUB_WORKSPACE}/libgit2/build/amd \ + ./hack/static.sh all + + TARGET_DIR=${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64 \ + BUILD_ROOT_DIR=${GITHUB_WORKSPACE}/libgit2/build/arm \ + TARGET_ARCH=arm64 \ + CMAKE_APPLE_SILICON_PROCESSOR=arm64 \ + ./hack/static.sh all + + mkdir -p ./libgit2-darwin/lib + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/include ./libgit2-darwin/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/share ./libgit2-darwin/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/cmake ./libgit2-darwin/lib/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/engines-3 ./libgit2-darwin/lib/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/ossl-modules ./libgit2-darwin/lib/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/pkgconfig ./libgit2-darwin/lib/ + + libtool -static -o ./libgit2-darwin/lib/libcrypto.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libcrypto.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libcrypto.a + libtool -static -o ./libgit2-darwin/lib/libgit2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libgit2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libgit2.a + libtool -static -o ./libgit2-darwin/lib/libssh2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libssh2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libssh2.a + libtool -static -o ./libgit2-darwin/lib/libssl.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libssl.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libssl.a + libtool -static -o ./libgit2-darwin/lib/libz.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libz.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libz.a + + tar -zcvf darwin-libs.tar.gz ./libgit2-darwin + + - name: Create Release + if: github.event_name != 'pull_request' + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + release_name: ${{ github.ref }} + tag_name: ${{ github.ref }} + draft: false + prerelease: true + - name: Upload Release Asset + if: github.event_name != 'pull_request' + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./darwin-libs.tar.gz + asset_name: darwin-libs.tar.gz + asset_content_type: application/gzip + build: runs-on: ubuntu-latest env: @@ -55,7 +137,7 @@ jobs: key: ${{ runner.os }}-buildx-ghcache-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx-ghcache- - - run: cat ./hack/Makefile + - run: cat ./hack/static.sh - name: Build candidate image id: build_candidate uses: docker/build-push-action@v2 diff --git a/README.md b/README.md index ed368d9..62b1066 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # golang-with-libgit2 -This repository contains a `Dockerfile` with two files: `Makefile` and `static.sh`. +This repository contains a `Dockerfile` with the statically built libgit2 and its dependency chain. + +The `hack` directory contains two main files: `Makefile` and `static.sh`. Both of which can be used to build the [libgit2][] dependency chain for **AMD64, ARM64 and ARMv7** binaries of Go projects that depend on [git2go][]. @@ -8,6 +10,8 @@ The `Makefile` is useful for development environments and will leverage OS speci The `static.sh` will build all `libgit2` dependencies from source using `musl` toolchain. This enables for a full static binary with the freedom of configuring each of the dependencies in chain. +Alternatively, the statically built libraries can be pulling from the produced images for Linux or from the github release artifacts for MacOS. + ### :warning: **Public usage discouraged** The set of dependencies was handpicked for the Flux project, based on the issue list documented below. While this setup @@ -47,17 +51,14 @@ while testing these against the git2go code before releasing the image. - [ ] [libgit2/git2go#836](https://github.com/libgit2/git2go/issues/836) - [ ] [libgit2/git2go#837](https://github.com/libgit2/git2go/issues/837) ---- -**NOTE** -The issues above do not affect libgit2 built with `static.sh` as all its +> **NOTE:** The issues above do not affect libgit2 built with `static.sh` as all its dependencies have been configured to be optimal for its use, as the first supported version of libgit2 is `1.3.0`. ---- ## Usage -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. +The [Dockerfile.test](./Dockerfile.test) file provides a working example on how to statically build a golang application that has a dependency on libgit2 and git2go. The example will statically build all dependencies based on the versions specified on `static.sh`. Then statically build the golang application and deploy it into an image based off `gcr.io/distroless/static`. diff --git a/hack/static.sh b/hack/static.sh index 1611de1..6a7cbb3 100755 --- a/hack/static.sh +++ b/hack/static.sh @@ -14,7 +14,7 @@ TARGET_DIR="${TARGET_DIR:-/usr/local/$(xx-info triple)}" BUILD_ROOT_DIR="${BUILD_ROOT_DIR:-/build}" SRC_DIR="${BUILD_ROOT_DIR}/src" -TARGET_ARCH="$(uname -m)" +TARGET_ARCH="${TARGET_ARCH:-$(uname -m)}" if command -v xx-info; then TARGET_ARCH="$(xx-info march)" fi @@ -59,21 +59,34 @@ function build_openssl(){ export OPENSSL_ROOT_DIR="${TARGET_DIR}" export OPENSSL_LIBRARIES="${TARGET_DIR}/lib" + export KERNEL_BITS=64 target_arch="" - if [ "${TARGET_ARCH}" = "armv7l" ]; then - # openssl does not have a specific armv7l - # using generic32 instead. - target_arch="linux-generic32" - elif [ "${TARGET_ARCH}" = "arm64" ] || [ "${TARGET_ARCH}" = "aarch64" ]; then - target_arch="linux-aarch64" - elif [ "${TARGET_ARCH}" = "x86_64" ]; then - target_arch="linux-x86_64" + if [[ ! $OSTYPE == darwin* ]]; then + if [ "${TARGET_ARCH}" = "armv7l" ]; then + # openssl does not have a specific armv7l + # using generic32 instead. + target_arch="linux-generic32" + export KERNEL_BITS=32 + elif [ "${TARGET_ARCH}" = "arm64" ] || [ "${TARGET_ARCH}" = "aarch64" ]; then + target_arch="linux-aarch64" + elif [ "${TARGET_ARCH}" = "x86_64" ]; then + target_arch="linux-x86_64" + fi else - echo "Architecture currently not supported: ${TARGET_ARCH}" - exit 1 + SUFFIX="" + if [ ! "${TARGET_ARCH}" = "$(uname -m)" ]; then + SUFFIX="-cc" + fi + + if [ "${TARGET_ARCH}" = "arm64" ] || [ "${TARGET_ARCH}" = "aarch64" ]; then + target_arch="darwin64-arm64${SUFFIX}" + elif [ "${TARGET_ARCH}" = "x86_64" ]; then + target_arch="darwin64-x86_64${SUFFIX}" + fi + # if none of the above, let openssl figure it out. fi - ./Configure "${target_arch}" threads no-shared zlib -fPIC -DOPENSSL_PIC \ + ./Configure "${target_arch}" threads no-shared no-stdio no-tests zlib -fPIC -DOPENSSL_PIC \ --prefix="${TARGET_DIR}" \ --with-zlib-include="${TARGET_DIR}/include" \ --with-zlib-lib="${TARGET_DIR}/lib" \ @@ -94,20 +107,28 @@ function build_libssh2(){ pushd build OPENSSL_LIBRARIES="${TARGET_DIR}/lib" - if [ "${TARGET_ARCH}" = "x86_64" ]; then + if [ "${TARGET_ARCH}" = "x86_64" ] && [[ ! $OSTYPE == darwin* ]]; then OPENSSL_LIBRARIES="${TARGET_DIR}/lib64" fi + # Set osx arch only when cross compiling on darwin + if [[ $OSTYPE == darwin* ]] && [ ! "${TARGET_ARCH}" = "$(uname -m)" ]; then + CMAKE_PARAMS=-DCMAKE_OSX_ARCHITECTURES="${TARGET_ARCH}" + fi + # Building examples allow for validating against missing symbols at compilation time. cmake "${CMAKE_PARAMS}" \ -DCMAKE_C_COMPILER="${C_COMPILER}" \ -DCMAKE_INSTALL_PREFIX="${TARGET_DIR}" \ - -DBUILD_SHARED_LIBS=OFF \ - -DLINT=OFF \ + -DBUILD_SHARED_LIBS:BOOL=OFF \ + -DLINT:BOOL=OFF \ + -DBUILD_EXAMPLES:BOOL=ON \ + -DBUILD_TESTING:BOOL=OFF \ -DCMAKE_C_FLAGS=-fPIC \ -DCRYPTO_BACKEND=OpenSSL \ - -DENABLE_ZLIB_COMPRESSION=ON \ + -DENABLE_ZLIB_COMPRESSION:BOOL=ON \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ + -DZLIB_LIBRARY="${TARGET_DIR}/lib/libz.a" \ -DOPENSSL_CRYPTO_LIBRARY="${OPENSSL_LIBRARIES}/libcrypto.a" \ -DOPENSSL_SSL_LIBRARY="${OPENSSL_LIBRARIES}/libssl.a" \ .. @@ -129,17 +150,21 @@ function build_libgit2(){ SSL_LIBRARY="${TARGET_DIR}/lib/libssl.a" CRYPTO_LIBRARY="${TARGET_DIR}/lib/libcrypto.a" - if [ "${TARGET_ARCH}" = "x86_64" ]; then + if [[ ! $OSTYPE == darwin* ]] && [ "${TARGET_ARCH}" = "x86_64" ]; then SSL_LIBRARY="${TARGET_DIR}/lib64/libssl.a" CRYPTO_LIBRARY="${TARGET_DIR}/lib64/libcrypto.a" fi + # Set osx arch only when cross compiling on darwin + if [[ $OSTYPE == darwin* ]] && [ ! "${TARGET_ARCH}" = "$(uname -m)" ]; then + CMAKE_PARAMS=-DCMAKE_OSX_ARCHITECTURES="${TARGET_ARCH}" + fi + cmake "${CMAKE_PARAMS}" \ -DCMAKE_C_COMPILER="${C_COMPILER}" \ -DCMAKE_INSTALL_PREFIX="${TARGET_DIR}" \ -DTHREADSAFE:BOOL=ON \ -DBUILD_CLAR:BOOL=OFF \ - -DBUILD_TESTS:BOOL=OFF \ -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON \ -DCMAKE_C_FLAGS=-fPIC \ @@ -151,9 +176,9 @@ function build_libgit2(){ -DREGEX_BACKEND:STRING=builtin \ -DOPENSSL_SSL_LIBRARY="${SSL_LIBRARY}" \ -DOPENSSL_CRYPTO_LIBRARY="${CRYPTO_LIBRARY}" \ - -DZLIB_LIBRARY="${TARGET_DIR}/lib/libz.a" \ -DCMAKE_INCLUDE_PATH="${TARGET_DIR}/include" \ -DCMAKE_LIBRARY_PATH="${TARGET_DIR}/lib" \ + -DCMAKE_PREFIX_PATH="${TARGET_DIR}" \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ .. From 71c683934c08a437d375c000eef3ae2f9c9c3811 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Mon, 7 Feb 2022 14:38:11 +0000 Subject: [PATCH 08/11] Set target OSX deployment Signed-off-by: Paulo Gomes --- .github/workflows/build.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e2643aa..732e4f4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,13 +4,14 @@ on: push: branches: - main + - testdarwin2 tags: - '**' - paths-ignore: - - README.md - pull_request: - paths-ignore: - - README.md + # paths-ignore: + # - README.md + # pull_request: + # paths-ignore: + # - README.md permissions: contents: write # needed to write releases @@ -70,6 +71,8 @@ jobs: ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libz.a tar -zcvf darwin-libs.tar.gz ./libgit2-darwin + env: + MACOSX_DEPLOYMENT_TARGET: 10.15 - name: Create Release if: github.event_name != 'pull_request' From 0716e02789da26ea1f47aed09a153d6d0a614735 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Mon, 7 Feb 2022 17:34:09 +0000 Subject: [PATCH 09/11] Remove no-stdio In linux no-stdio resulted in some intermittent issues whilst using libgit2. Signed-off-by: Paulo Gomes --- hack/static.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/static.sh b/hack/static.sh index 6a7cbb3..1a99094 100755 --- a/hack/static.sh +++ b/hack/static.sh @@ -86,7 +86,7 @@ function build_openssl(){ # if none of the above, let openssl figure it out. fi - ./Configure "${target_arch}" threads no-shared no-stdio no-tests zlib -fPIC -DOPENSSL_PIC \ + ./Configure "${target_arch}" threads no-shared no-tests zlib -fPIC -DOPENSSL_PIC \ --prefix="${TARGET_DIR}" \ --with-zlib-include="${TARGET_DIR}/include" \ --with-zlib-lib="${TARGET_DIR}/lib" \ From da24a177b7a170252e5f85173793c1f1de8151a7 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Tue, 8 Feb 2022 09:30:14 +0000 Subject: [PATCH 10/11] Fix build targets Signed-off-by: Paulo Gomes --- .github/workflows/build.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 732e4f4..64fa1fe 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,14 +4,14 @@ on: push: branches: - main - - testdarwin2 + - libgit2-* tags: - '**' - # paths-ignore: - # - README.md - # pull_request: - # paths-ignore: - # - README.md + paths-ignore: + - README.md + pull_request: + paths-ignore: + - README.md permissions: contents: write # needed to write releases From 52e9a938e1912b7b2f08bf1ed42ab4e991e34fb0 Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Tue, 8 Feb 2022 19:22:03 +0000 Subject: [PATCH 11/11] Split macos release workflow Signed-off-by: Paulo Gomes --- .github/workflows/build.yaml | 68 ++-------------------- .github/workflows/release.yaml | 102 +++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 64fa1fe..6ce3298 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -4,7 +4,6 @@ on: push: branches: - main - - libgit2-* tags: - '**' paths-ignore: @@ -14,89 +13,32 @@ on: - README.md permissions: - contents: write # needed to write releases packages: write # needed for ghcr access jobs: mac-build: - # This job builds and releases "universal libraries" that are - # supported by both darwin-amd64 and darwin-arm64. - # - # First builds in amd64, then cross-compile in arm64. Later combining - # both outcomes onto a single binary for each static library. - # - # `macos-11` has been picked as support for arm64 was only added on Xcode 12. - # Although some minor versions of Catalina 10.15 can support it, at the time - # of testing, GitHub's macos-10.15 did not seem to. - # Cross-compiling to arm64 on that runner consistently failed. runs-on: macos-11 steps: - name: Checkout uses: actions/checkout@v2 - - name: Build universal static libraries for Darwin + - name: Test build script for darwin-amd64 run: | TARGET_DIR=${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64 \ BUILD_ROOT_DIR=${GITHUB_WORKSPACE}/libgit2/build/amd \ ./hack/static.sh all - + env: + MACOSX_DEPLOYMENT_TARGET: 10.15 + - name: Test cross compile script for darwin-arm64 + run: | TARGET_DIR=${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64 \ BUILD_ROOT_DIR=${GITHUB_WORKSPACE}/libgit2/build/arm \ TARGET_ARCH=arm64 \ CMAKE_APPLE_SILICON_PROCESSOR=arm64 \ ./hack/static.sh all - - mkdir -p ./libgit2-darwin/lib - mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/include ./libgit2-darwin/ - mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/share ./libgit2-darwin/ - mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/cmake ./libgit2-darwin/lib/ - mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/engines-3 ./libgit2-darwin/lib/ - mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/ossl-modules ./libgit2-darwin/lib/ - mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/pkgconfig ./libgit2-darwin/lib/ - - libtool -static -o ./libgit2-darwin/lib/libcrypto.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libcrypto.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libcrypto.a - libtool -static -o ./libgit2-darwin/lib/libgit2.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libgit2.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libgit2.a - libtool -static -o ./libgit2-darwin/lib/libssh2.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libssh2.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libssh2.a - libtool -static -o ./libgit2-darwin/lib/libssl.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libssl.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libssl.a - libtool -static -o ./libgit2-darwin/lib/libz.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libz.a \ - ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libz.a - - tar -zcvf darwin-libs.tar.gz ./libgit2-darwin env: MACOSX_DEPLOYMENT_TARGET: 10.15 - - name: Create Release - if: github.event_name != 'pull_request' - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - release_name: ${{ github.ref }} - tag_name: ${{ github.ref }} - draft: false - prerelease: true - - name: Upload Release Asset - if: github.event_name != 'pull_request' - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./darwin-libs.tar.gz - asset_name: darwin-libs.tar.gz - asset_content_type: application/gzip - build: runs-on: ubuntu-latest env: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..2905cc9 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,102 @@ +--- +name: 'Release MacOS static libraries' +on: + push: + tags: + - '**' + + workflow_dispatch: + inputs: + tag: + description: 'release prefix' + default: 'rc' + required: true + +permissions: + contents: write # needed to write releases + +jobs: + + mac-build: + # This job builds and releases "universal libraries" that are + # supported by both darwin-amd64 and darwin-arm64. + # + # First builds in amd64, then cross-compile in arm64. Later combining + # both outcomes onto a single binary for each static library. + # + # `macos-11` has been picked as support for arm64 was only added on Xcode 12. + # Although some minor versions of Catalina 10.15 can support it, at the time + # of testing, GitHub's macos-10.15 did not seem to. + # Cross-compiling to arm64 on that runner consistently failed. + runs-on: macos-11 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Build universal static libraries for Darwin + run: | + TARGET_DIR=${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64 \ + BUILD_ROOT_DIR=${GITHUB_WORKSPACE}/libgit2/build/amd \ + ./hack/static.sh all + + TARGET_DIR=${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64 \ + BUILD_ROOT_DIR=${GITHUB_WORKSPACE}/libgit2/build/arm \ + TARGET_ARCH=arm64 \ + CMAKE_APPLE_SILICON_PROCESSOR=arm64 \ + ./hack/static.sh all + + mkdir -p ./libgit2-darwin/lib + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/include ./libgit2-darwin/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/share ./libgit2-darwin/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/cmake ./libgit2-darwin/lib/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/engines-3 ./libgit2-darwin/lib/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/ossl-modules ./libgit2-darwin/lib/ + mv ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/pkgconfig ./libgit2-darwin/lib/ + + libtool -static -o ./libgit2-darwin/lib/libcrypto.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libcrypto.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libcrypto.a + libtool -static -o ./libgit2-darwin/lib/libgit2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libgit2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libgit2.a + libtool -static -o ./libgit2-darwin/lib/libssh2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libssh2.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libssh2.a + libtool -static -o ./libgit2-darwin/lib/libssl.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libssl.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libssl.a + libtool -static -o ./libgit2-darwin/lib/libz.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-amd64/lib/libz.a \ + ${GITHUB_WORKSPACE}/build/libgit2-darwin-arm64/lib/libz.a + + tar -zcvf darwin-libs.tar.gz ./libgit2-darwin + env: + MACOSX_DEPLOYMENT_TARGET: 10.15 + + - name: Prepare + id: prep + run: | + VERSION="${{ github.event.inputs.tag }}-${GITHUB_SHA::8}" + if [[ $GITHUB_REF == refs/tags/* ]]; then + VERSION=${GITHUB_REF/refs\/tags\//} + fi + echo ::set-output name=VERSION::${VERSION} + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + release_name: ${{ steps.prep.outputs.VERSION }} + tag_name: ${{ steps.prep.outputs.VERSION }} + draft: false + prerelease: true + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./darwin-libs.tar.gz + asset_name: darwin-libs.tar.gz + asset_content_type: application/gzip