From 47fb33baf07ff359ed0c1c62aef03a70b7989d16 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Sun, 7 Jun 2020 00:39:27 +0100 Subject: 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. --- .github/workflows/main.yml | 202 ++++++++++++++++++++++++++++++++++++++++ azure-pipelines/build.sh | 4 + azure-pipelines/getcontainer.sh | 45 +++++++++ azure-pipelines/setup-mingw.sh | 14 ++- 4 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100755 azure-pipelines/getcontainer.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..02f19e2c6 --- /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 27e2f3e38..bec855d4a 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 000000000..bc93f4919 --- /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 1172c2077..d500da058 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" -- cgit v1.2.1