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 870c658..8c92d87 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,107 +1,59 @@ -# 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 - -FROM golang:${GO_VERSION}-${BASE_VARIANT} as gostable - -FROM gostable AS go-linux - -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 / / +ARG LIBGIT2_IMG +ARG LIBGIT2_TAG -ARG TARGETPLATFORM - -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++ +FROM ${LIBGIT2_IMG}:${LIBGIT2_TAG} AS build-deps -RUN CHOST=$(xx-clang --print-target-triple) \ - ./static.sh build_libz - -RUN CHOST=$(xx-clang --print-target-triple) \ - ./static.sh build_openssl +FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx -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" +FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} as gostable -RUN ./static.sh build_libssh2 -RUN ./static.sh build_libgit2 +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 build +RUN apk add clang lld pkgconfig -# Copy cross-compilation tools COPY --from=xx / / -# Copy compiled libraries -COPY --from=build-deps /usr/local/ /usr/local/ -RUN apk add clang lld pkgconfig +# 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 --no-cache \ - musl-dev \ - gcc +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 @@ -119,11 +71,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..d99ede8 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,42 @@ -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 +MUSL-CC = + +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) + 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 + +GO_STATIC_FLAGS=-tags 'netgo,osusergo,static_build' + + .PHONY: build build: docker buildx build \ @@ -17,7 +49,9 @@ 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 \ $(BUILD_ARGS) . @@ -33,3 +67,24 @@ builder: --use # install qemu emulators docker run -it --rm --privileged tonistiigi/binfmt --install all + + +$(LIBGIT2): $(MUSL-CC) +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 + +$(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) + cd tests/smoketest; go vet $(GO_STATIC_FLAGS) ./... + cd tests/smoketest; go run $(GO_STATIC_FLAGS) main.go diff --git a/hack/download-musl.sh b/hack/download-musl.sh new file mode 100755 index 0000000..de6b4a3 --- /dev/null +++ b/hack/download-musl.sh @@ -0,0 +1,36 @@ +#!/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_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_FILENAME}" + mkdir -p "${MUSL_DIR}" + + echo "${MUSL_SHA512} ${TARGET_FILE}" + curl -o "${TARGET_FILE}" -LO "${MUSL_AARCH64_URL}" + if ! echo "${MUSL_SHA512} ${TARGET_FILE}" | sha512sum --check; then + echo "Checksum failed for ${MUSL_FILENAME}." + rm -rf "${MUSL_DIR}" + exit 1 + fi + + tar xzf "${TARGET_FILE}" -C "${MUSL_DIR}" + rm "${TARGET_FILE}" +fi diff --git a/hack/extract-libraries.sh b/hack/extract-libraries.sh new file mode 100755 index 0000000..8458847 --- /dev/null +++ b/hack/extract-libraries.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +set -eoux pipefail + +IMG_TAG="${IMG_TAG:-.}" + +function extract(){ + PLATFORM=$1 + DIR=$2 + + id=$(docker create --platform="${PLATFORM}" "${IMG_TAG}" sh) + 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 38e697d..119c814 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 3b5b963..fa5bca8 100644 --- a/tests/smoketest/main.go +++ b/tests/smoketest/main.go @@ -12,6 +12,7 @@ import ( "io" "io/ioutil" "log" + "net" "net/url" "os" "path/filepath" @@ -29,12 +30,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) @@ -65,6 +68,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) @@ -133,11 +137,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 @@ -164,7 +168,10 @@ 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) git2go.ErrorCode { + fmt.Printf("[knownHostsCallback] valid: %v hostname: %q\n", valid, hostname) + if cert == nil { + fmt.Printf("Cert is nil\n") return git2go.ErrorCodeCertificate } @@ -175,13 +182,27 @@ func knownHostsCallback(host string, knownHosts []byte) git2go.CertificateCheckC fmt.Printf("Known keys: %d\n", len(kh)) - // Check if the configured host matches the hostname given to - // the callback. - // In libgit 1.1.* hostname also includes its with port. - if host != hostname { + // First, attempt to split the configured host and port to validate + // the port-less hostname given to the callback. + 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. + 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", - host, - hostname) + hostWithoutPort, + hostnameWithoutPort) + } + + if hostnameWithoutPort != hostWithoutPort { return git2go.ErrorCodeUser } @@ -190,6 +211,7 @@ func knownHostsCallback(host string, knownHosts []byte) git2go.CertificateCheckC // includes the port), and normalize it, so we can check if there // is an entry for the hostname _and_ port. h := knownhosts.Normalize(host) + fmt.Printf("normalised host (with port): %q\n", h) for _, k := range kh { if k.matches(h, cert.Hostkey) { return git2go.ErrorCodeOK @@ -234,6 +256,7 @@ func parseKnownHosts(s string) ([]knownKey, error) { func (k knownKey) matches(host string, hostkey git2go.HostkeyCertificate) bool { if !containsHost(k.hosts, host) { + fmt.Printf("host not found: %q\n", host) return false }