Commit 47fb33baf07ff359ed0c1c62aef03a70b7989d16

Edward Thomson 2020-06-07T00:39:27

Introduce CI with GitHub Actions Add CI using GitHub Actions and GitHub Packages: * This moves our Linux build containers into GitHub Packages; we will identify the most recent commit that updated the docker descriptions, and then look for a docker image in libgit2's GitHub Packages registry for a container with the tag corresponding to that description. If there is not one, we will build the container and then push it to GitHub Packages. * We no longer need to manage authentication with our own credentials or PAT tokens. GitHub Actions provides a GITHUB_TOKEN that can publish artifacts, packages and commits to our repository within a workflow run. * We will use a matrix to build our various CI steps. This allows us to keep configuration in a single place without multiple YAML files.

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..02f19e2
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,202 @@
+# Continuous integration and pull request validation builds for the
+# master and maintenance branches.
+name: CI Build
+
+on:
+  push:
+    branches: [ master, maint/* ]
+  pull_request:
+    branches: [ master, maint/* ]
+
+env:
+  docker-registry: docker.pkg.github.com
+  docker-config-path: azure-pipelines/docker
+
+jobs:
+  # Build the docker container images that we will use for our Linux
+  # builds.  This will identify the last commit to the repository that
+  # updated the docker images, and try to download the image tagged with
+  # that sha.  If it does not exist, we'll do a docker build and push
+  # the image up to GitHub Packages for the actual CI/CD runs.  We tag
+  # with both the sha and "latest" so that the subsequent runs need not
+  # know the sha.  Only do this on CI builds (when the event is a "push")
+  # because PR builds from forks lack permission to write packages.
+  build_containers:
+    name: Create docker image
+    strategy:
+      matrix:
+        container:
+        - xenial
+        - bionic
+        - docurium
+    runs-on: ubuntu-latest
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v2
+      with:
+        fetch-depth: 0
+      if: github.event_name == 'push'
+    - name: Download existing container
+      run: azure-pipelines/getcontainer.sh ${{ env.docker-config-path }}/${{ matrix.container }}
+      env:
+        DOCKER_REGISTRY: ${{ env.docker-registry }}
+        GITHUB_TOKEN: ${{ secrets.github_token }}
+      if: github.event_name == 'push'
+    - name: Build and publish image
+      run: |
+        docker build -t ${{ env.docker-registry-container-sha }} --build-arg BASE=${{ matrix.container.base }} -f ${{ matrix.container }} .
+        docker push ${{ env.docker-registry-container-sha }}
+      working-directory: ${{ env.docker-config-path }}
+      if: github.event_name == 'push' && env.docker-container-exists != 'true'
+
+  # Run our CI/CD builds.  We build a matrix with the various build targets
+  # and their details.  Then we build either in a docker container (Linux)
+  # or on the actual hosts (macOS, Windows).
+  build:
+    name: Build
+    needs: [build_containers]
+    strategy:
+      matrix:
+        platform:
+        - # Xenial, GCC, OpenSSL
+          image: xenial
+          env:
+            CC: gcc
+            CMAKE_GENERATOR: Ninja
+            CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+          os: ubuntu-latest
+        - # Xenial, GCC, mbedTLS
+          image: xenial
+          env:
+            CC: gcc
+            CMAKE_GENERATOR: Ninja
+            CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+          os: ubuntu-latest
+        - # Xenial, Clang, OpenSSL
+          image: xenial
+          env:
+            CC: clang
+            CMAKE_GENERATOR: Ninja
+            CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+          os: ubuntu-latest
+        - # Xenial, Clang, mbedTLS
+          image: xenial
+          env:
+            CC: clang
+            CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+            CMAKE_GENERATOR: Ninja
+          os: ubuntu-latest
+        - # macOS
+          os: macos-10.15
+          env:
+            CC: clang
+            CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
+            CMAKE_GENERATOR: Ninja
+            PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+            SKIP_SSH_TESTS: true
+            SKIP_NEGOTIATE_TESTS: true
+          setup-script: osx
+        - # Windows amd64 Visual Studio
+          os: windows-2019
+          env:
+            ARCH: amd64
+            CMAKE_GENERATOR: Visual Studio 16 2019
+            CMAKE_OPTIONS: -A x64 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON
+            SKIP_SSH_TESTS: true
+            SKIP_NEGOTIATE_TESTS: true
+        - # Windows x86 Visual Studio
+          os: windows-2019
+          env:
+            ARCH: x86
+            CMAKE_GENERATOR: Visual Studio 16 2019
+            CMAKE_OPTIONS: -A Win32 -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
+            SKIP_SSH_TESTS: true
+            SKIP_NEGOTIATE_TESTS: true
+        - # Windows amd64 mingw
+          os: windows-2019
+          setup-script: mingw
+          env:
+            ARCH: amd64
+            CMAKE_GENERATOR: MinGW Makefiles
+            CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+            BUILD_TEMP: D:\Temp
+            BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+            SKIP_SSH_TESTS: true
+            SKIP_NEGOTIATE_TESTS: true
+        - # Windows x86 mingw
+          os: windows-2019
+          setup-script: mingw
+          env:
+            ARCH: x86
+            CMAKE_GENERATOR: MinGW Makefiles
+            CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+            BUILD_TEMP: D:\Temp
+            BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+            SKIP_SSH_TESTS: true
+            SKIP_NEGOTIATE_TESTS: true
+      fail-fast: false
+    env: ${{ matrix.platform.env }}
+    runs-on: ${{ matrix.platform.os }}
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v2
+      with:
+        fetch-depth: 0
+    - name: Set up build environment
+      run: azure-pipelines/setup-${{ matrix.platform.setup-script }}.sh
+      shell: bash
+      if: matrix.platform.setup-script != ''
+    - name: Download container
+      run: azure-pipelines/getcontainer.sh ${{ env.docker-config-path }}/${{ matrix.platform.image }}
+      env:
+        DOCKER_REGISTRY: ${{ env.docker-registry }}
+        GITHUB_TOKEN: ${{ secrets.github_token }}
+      if: matrix.platform.image != ''
+    - name: Create container
+      run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ matrix.platform.image }} .
+      working-directory: ${{ env.docker-config-path }}
+      if: matrix.platform.image != '' && env.docker-container-exists != 'true'
+    - name: Build and test
+      run: |
+        export GITTEST_NEGOTIATE_PASSWORD="${{ secrets.GITTEST_NEGOTIATE_PASSWORD }}"
+
+        if [ -n "${{ matrix.platform.image }}" ]; then
+          docker run -v $(pwd):/home/libgit2/source -w /home/libgit2/source -e CC -e CMAKE_GENERATOR -e CMAKE_OPTIONS -e PKG_CONFIG_PATH -e GITTEST_NEGOTIATE_PASSWORD -e SKIP_SSH_TESTS -e SKIP_NEGOTIATE_TESTS ${{ env.docker-registry-container-sha }} /bin/bash -c "mkdir build && cd build && ../azure-pipelines/build.sh && ../azure-pipelines/test.sh"
+        else
+          mkdir build && cd build
+          ../azure-pipelines/build.sh
+          ../azure-pipelines/test.sh
+        fi
+      shell: bash
+
+  # Generate documentation using docurium.  We'll upload the documentation
+  # as a build artifact so that it can be reviewed as part of a pull
+  # request or in a forked build.  For CI builds in the main repository's
+  # master branch, we'll push the gh-pages branch back up so that it is
+  # published to our documentation site.
+  documentation:
+    name: Generate documentation
+    needs: [build_containers]
+    runs-on: ubuntu-latest
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v2
+      with:
+        fetch-depth: 0
+    - name: Generate documentation
+      run: |
+        git config user.name 'Documentation Generation'
+        git config user.email 'libgit2@users.noreply.github.com'
+        git branch gh-pages origin/gh-pages
+        docker login https://${{ env.docker-registry }} -u ${{ github.actor }} -p ${{ github.token }}
+        docker run --rm -v $(pwd):/home/libgit2/source -w /home/libgit2/source ${{ env.docker-registry }}/${{ github.repository }}/docurium:latest cm doc api.docurium
+        git checkout gh-pages
+        zip --exclude .git/\* --exclude .gitignore --exclude .gitattributes -r api-documentation.zip .
+    - uses: actions/upload-artifact@v2
+      name: Upload artifact
+      with:
+        name: api-documentation
+        path: api-documentation.zip
+    - name: Push documentation branch
+      run: git push origin gh-pages
+      if: github.event_name == 'push' && github.repository == 'libgit2/libgit2'
diff --git a/azure-pipelines/build.sh b/azure-pipelines/build.sh
index 27e2f3e..bec855d 100755
--- a/azure-pipelines/build.sh
+++ b/azure-pipelines/build.sh
@@ -13,6 +13,10 @@ BUILD_PATH=${BUILD_PATH:=$PATH}
 CMAKE=$(which cmake)
 CMAKE_GENERATOR=${CMAKE_GENERATOR:-Unix Makefiles}
 
+if [[ "$(uname -s)" == MINGW* ]]; then
+	BUILD_PATH=$(cygpath "$BUILD_PATH")
+fi
+
 indent() { sed "s/^/    /"; }
 
 echo "Source directory: ${SOURCE_DIR}"
diff --git a/azure-pipelines/getcontainer.sh b/azure-pipelines/getcontainer.sh
new file mode 100755
index 0000000..bc93f49
--- /dev/null
+++ b/azure-pipelines/getcontainer.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+set -e
+
+DOCKERFILE_PATH=$1
+
+if [ "${DOCKERFILE_PATH}" = "" ]; then
+	echo "usage: $0 dockerfile"
+	exit 1
+fi
+
+if [ "${DOCKER_REGISTRY}" = "" ]; then
+	echo "DOCKER_REGISTRY environment variable is unset."
+	echo "Not running inside GitHub Actions or misconfigured?"
+	exit 1
+fi
+
+DOCKER_CONTAINER="${GITHUB_REPOSITORY}/$(basename ${DOCKERFILE_PATH})"
+DOCKER_REGISTRY_CONTAINER="${DOCKER_REGISTRY}/${DOCKER_CONTAINER}"
+
+echo "::set-env name=docker-container::${DOCKER_CONTAINER}"
+echo "::set-env name=docker-registry-container::${DOCKER_REGISTRY_CONTAINER}"
+
+# Identify the last git commit that touched the Dockerfiles
+# Use this as a hash to identify the resulting docker containers
+DOCKER_SHA=$(git log -1 --pretty=format:"%h" -- "${DOCKERFILE_PATH}")
+echo "::set-env name=docker-sha::${DOCKER_SHA}"
+
+DOCKER_REGISTRY_CONTAINER_SHA="${DOCKER_REGISTRY_CONTAINER}:${DOCKER_SHA}"
+
+echo "::set-env name=docker-registry-container-sha::${DOCKER_REGISTRY_CONTAINER_SHA}"
+echo "::set-env name=docker-registry-container-latest::${DOCKER_REGISTRY_CONTAINER}:latest"
+
+exists="true"
+docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false"
+
+if [ "${exists}" != "false" ]; then
+	docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false"
+fi
+
+if [ "${exists}" = "true" ]; then
+	echo "::set-env name=docker-container-exists::true"
+else
+	echo "::set-env name=docker-container-exists::false"
+fi
diff --git a/azure-pipelines/setup-mingw.sh b/azure-pipelines/setup-mingw.sh
index 1172c20..d500da0 100755
--- a/azure-pipelines/setup-mingw.sh
+++ b/azure-pipelines/setup-mingw.sh
@@ -4,6 +4,9 @@ echo "##########################################################################
 echo "## Downloading mingw"
 echo "##############################################################################"
 
+BUILD_TEMP=${BUILD_TEMP:=$TEMP}
+BUILD_TEMP=$(cygpath $BUILD_TEMP)
+
 case "$ARCH" in
 	amd64)
 		MINGW_URI="https://bintray.com/libgit2/build-dependencies/download_file?file_path=mingw-w64-x86_64-8.1.0-release-win32-seh-rt_v6-rev0.zip";;
@@ -11,5 +14,12 @@ case "$ARCH" in
 		MINGW_URI="https://bintray.com/libgit2/build-dependencies/download_file?file_path=mingw-w64-i686-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
 esac
 
-curl -s -L "$MINGW_URI" -o "$TEMP"/mingw-"$ARCH".zip
-unzip -q "$TEMP"/mingw-"$ARCH".zip -d "$TEMP"
+if [ -z "$MINGW_URI" ]; then
+	echo "No URL"
+	exit 1
+fi
+
+mkdir -p "$BUILD_TEMP"
+
+curl -s -L "$MINGW_URI" -o "$BUILD_TEMP"/mingw-"$ARCH".zip
+unzip -q "$BUILD_TEMP"/mingw-"$ARCH".zip -d "$BUILD_TEMP"