lncm/invoicer

View on GitHub
.github/workflows/on-tag.yml

Summary

Maintainability
Test Coverage
name: Build & deploy on git tag push

env:
  APP: invoicer
  ACTIONS_ALLOW_UNSECURE_COMMANDS: true

  # Capture groups in $TAG_FMT:
  #   \1 => TAG       vX.Y.Z+build<N>
  #   \2 => VERSION   vX.Y.Z
  #   \3 => ignore    (captures dot, and last number-group in version)
  #   \4 => BUILD     N
  TAG_FMT: '^refs/tags/((v(.?[0-9]+){3})\+build([0-9]+))$'

on:
  push:
    tags: [ '*' ]

jobs:
  build:
    name: Build invoicer
    runs-on: ubuntu-18.04

    strategy:
      matrix:
        arch:
          - amd64
          - arm32v6
          - arm32v7
          - arm64v8

    env:
      QEMU_VERSION: v4.2.0
      DOCKER_BUILDKIT: 1

    steps:
      - uses: actions/checkout@v2

      - name: Setup environment
        run: |
          if ! echo "$GITHUB_REF" | grep -qE "$TAG_FMT"; then
            echo "ERR: TAG must be in format: vX.Y.Z+build<N>"
            exit 1
          fi

          VERSION="$(echo "$GITHUB_REF" | sed -E "s|$TAG_FMT|\2|")"
          DIR="$(echo "${VERSION#v}" | cut -d. -f-2)"

          if ! grep -q "^ARG VERSION=$VERSION$" "$DIR/Dockerfile"; then
            echo "ERR: $DIR/Dockerfile must contain VERSION=$VERSION"
            exit 1
          fi

          echo ::set-env name=DIR::"$DIR"

          echo ::set-env name=TAG::"$(echo "$GITHUB_REF" | sed -E "s|$TAG_FMT|\1|")"
          echo ::set-env name=BUILD::"$(echo "$GITHUB_REF" | sed -E "s|$TAG_FMT|\4|")"

      # GOARCH => arm|arm64
      # GOARM  => 6|7
      - name: Setup Go environment
        if: matrix.arch != 'amd64'
        env:
          ARCH: ${{ matrix.arch }}
        run: |
          GOARCH="${ARCH%32v?}"
          echo ::set-env name=GOARCH::"${GOARCH%v8}"
          echo ::set-env name=GOARM::"$(echo "$ARCH" | sed -En 's|^arm32v([6-7])$|\1|p')"

      - name: Print ENV VARs set above
        run: |
          printf "    APP: %s\n"  "$APP"
          printf "   ARCH: %s\n"  "${{ matrix.arch }}"
          printf "    TAG: %s\n"  "$TAG"
          printf "    DIR: %s\n"  "$DIR"
          printf "  BUILD: %s\n"  "$BUILD"
          printf " GOARCH: %s\n"  "$GOARCH"
          printf "  GOARM: %s\n"  "$GOARM"

      - name: Build ${{ env.APP }}
        run: >
          docker build --no-cache .
          --build-arg "ARCH=$ARCH"
          --build-arg "GOARCH=$GOARCH"
          --build-arg "GOARM=$GOARM"
          --label     "arch=${{ matrix.arch }}"
          --label     "commit=${{ github.sha }}"
          --label     "git-tag=$TAG"
          --label     "guilty=${{ github.actor }}"
          --label     "repo-url=${{ github.repositoryUrl }}"
          --tag       "$APP"

      - name: Show built image details
        run: docker images "$APP"

      - name: Register self-compiled qemu
        if: matrix.arch != 'amd64'
        env:
          ARCH: ${{ matrix.arch }}
        run: docker run --rm --privileged "meedamian/simple-qemu:$QEMU_VERSION-${ARCH%32v6}" -p yes

      - name: Run sanity checks
        env:
          DIR: /usr/local/bin
        run: |
          run() {
            ENTRYPOINT="${1:-$APP}"; shift;
            ARGS=${*:-"--version"}

            printf "\n$ %s %s\n" "$ENTRYPOINT" "$ARGS"
            docker run --rm  --entrypoint "$ENTRYPOINT"  "$APP"  $ARGS
            printf "\n"
          }

          docker inspect "$APP" | jq '.'
          printf "\n"

          run invoicer

          run uname -a
          run cat /etc/os-release
          run sha256sum "$DIR/invoicer"

          docker run --rm --entrypoint=sh -u=root "$APP" -c "apk add --no-cache file && file $DIR/invoicer"

      - name: Save image to a .tgz file
        run: |
          mkdir -p images/

          docker tag  "$APP"  "$APP:${{ matrix.arch }}"
          docker save "$APP:${{ matrix.arch }}" | gzip > "images/docker-$APP-$TAG-${{ matrix.arch }}.tgz"

      - name: Print sha256sum of built image
        run: sha256sum images/*

      - name: Upload docker image as build artifact
        uses: actions/upload-artifact@v1.0.0
        with:
          name: docker-images
          path: images/

      - name: Extract binary from the built image
        run: |
          mkdir -p binaries/
          ID=$(docker create "$APP:${{ matrix.arch }}")
          docker cp  "$ID:/usr/local/bin/invoicer"  binaries/
          docker rm  "$ID"

          gzip -S "-$TAG-${{ matrix.arch }}.gz" "binaries/$APP"

      - name: Print sha256sum of extracted binaries
        run: sha256sum binaries/*

      - name: Upload raw binaries as build artifacts
        uses: actions/upload-artifact@v1.0.0
        with:
          name: binaries
          path: binaries/

  deploy:
    name: Deploy to Docker Hub & Github Releases.  Only after successful build.

    runs-on: ubuntu-18.04
    needs: build

    env:
      DOCKER_CLI_EXPERIMENTAL: enabled

    steps:
      - name: Setup environment
        run: |
          echo ::set-env name=SLUG::"$(echo ${GITHUB_REPOSITORY,,} | sed 's/docker-//')"

          echo ::set-env name=TAG::"$(echo "$GITHUB_REF" | sed -E "s|$TAG_FMT|\1|")"
          echo ::set-env name=VERSION::"$(echo "$GITHUB_REF" | sed -E "s|$TAG_FMT|\2|")"
          echo ::set-env name=BUILD::"$(echo "$GITHUB_REF" | sed -E "s|$TAG_FMT|\4|")"

      - name: Print just set ENV VARs
        run: |
          printf "    APP: %s\n"  "$APP"
          printf "    TAG: %s\n"  "$TAG"
          printf "   SLUG: %s\n"  "$SLUG"
          printf "VERSION: %s\n"  "$VERSION"
          printf "  BUILD: %s\n"  "$BUILD"

      - name: Download all build artifacts
        uses: actions/download-artifact@v1.0.0
        with:
          name: docker-images

      - name: Print sha256sum of downloaded images
        run: sha256sum docker-images/*

      - name: Load images locally
        run: find docker-images  -exec docker load -i "{}" \;

      # `invoicer:arm64` -> `lncm/invoicer:v0.6.0-arm64[+build<N>]`
      - name: Version-tag all images
        run: |
          for arch in $(docker images ${APP} --format "{{.Tag}}"); do
            docker tag  "$APP:$arch"  "$SLUG:$VERSION-$arch-build$BUILD"
            docker tag  "$APP:$arch"  "$SLUG:$VERSION-$arch"
          done

      - name: List all tagged images
        run: docker images "$SLUG"

      - name: Login to Docker Hub
        env:
          DOCKER_USER: meedamian
        run: |
          echo "Logging in as ${DOCKER_USER}…"
          echo "${{ secrets.DOCKER_TOKEN }}" | docker login -u="$DOCKER_USER" --password-stdin

      - name: Push all images
        run: docker images "$SLUG" --format "{{.Repository}}:{{.Tag}}" | xargs -I %  docker push %

      - name: Get convenience Docker tag suggestions
        id: tags
        uses: meeDamian/tag-suggestions@v1.0.3

      - name: Create :X.Y.Z manifest
        run: ./scripts/ci-create-manifest.sh  "$SLUG"  "$VERSION"

      - name: Create :X.Y manifest
        if: steps.tags.outputs.minor != ''
        run: ./scripts/ci-create-manifest.sh "$SLUG" "$VERSION" "${{ steps.tags.outputs.minor }}"

      - name: Create :X manifest
        if: steps.tags.outputs.major != ''
        run: ./scripts/ci-create-manifest.sh "$SLUG" "$VERSION" "${{ steps.tags.outputs.major }}"

      - name: Create :latest manifest
        if: steps.tags.outputs.latest != ''
        run: ./scripts/ci-create-manifest.sh  "$SLUG" "$VERSION" "${{ steps.tags.outputs.latest }}"

      - name: List all tagged images
        run: docker images "$SLUG"


      - name: Download binaries from build artifacts
        uses: actions/download-artifact@v1.0.0
        with:
          name: binaries

      - name: Print checksums of everything
        run: sha256sum  binaries/*  docker-images/*

      - name: Upload binaries to Github Release
        uses: meeDamian/github-release@v1.0.1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          name: ${{ env.VERSION }}
          prerelease: true
          gzip: false
          files: >
            docker-images/*
            binaries/*