diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 00000000..0fc2f7e7 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,24 @@ +# CI Workflows + +## Overview + +- [Build](build.yml) + - Build and test Haskell code + - Build Docker image +- [Draft](draft.yml) + - Create a GH draft release with a static binary +- [Release](release.yml) + - Upload the Docker image ghcr.io + - Upload the package and docs to Hackage + +## Events + +```mermaid +graph LR + event[GH Event]-->|on push|Build + event-->|tag created|Draft + Draft-->|create draft release|End + event-->|release published|Release + Release-->|upload artifacts to Hackage/GHCR|End + Build-->End +``` diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..def87928 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,68 @@ +# The present workflow was made based on the following references: +# - https://github.com/actions/cache/blob/main/examples.md#haskell---cabal +# - https://github.com/haskell/time/blob/master/.github/workflows/ci.yml +# - https://github.com/stackbuilders/stache/blob/master/.github/workflows/ci.yaml +# - https://markkarpov.com/post/github-actions-for-haskell-ci.html +--- +name: Build + +on: push + +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: true + +jobs: + haskell: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - macos-latest + - ubuntu-latest + ghc: + - "9.0" + - "8.10" + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install dependencies (Linux) + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt-get update + sudo apt-get install zsh + - name: Install dependencies (macOS) + if: ${{ runner.os == 'macOS' }} + run: | + brew update + brew install zsh + - name: Install Haskell tooling + uses: haskell/actions/setup@v2 + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: "3.6" + - name: Configure project + run: cabal configure --enable-tests + - name: Freeze dependencies + run: cabal freeze + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cabal/package + ~/.cabal/store + dist-newstyle + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} + restore-keys: ${{ runner.os }}-${{ matrix.ghc }}- + - name: Build project + run: cabal build + - name: Run tests + run: cabal test + - name: Check documentation + run: ./script/haddock + + docker: + uses: ./.github/workflows/reusable-docker.yml + with: + push: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 9e7cc480..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,80 +0,0 @@ -# The present workflow was made based on the following references: -# - https://github.com/actions/cache/blob/main/examples.md#haskell---cabal -# - https://github.com/haskell/time/blob/master/.github/workflows/ci.yml -# - https://github.com/stackbuilders/stache/blob/master/.github/workflows/ci.yaml -# - https://markkarpov.com/post/github-actions-for-haskell-ci.html -# - https://github.com/tfausak/strive/blob/main/.github/workflows/ci.yaml -# - https://hackage.haskell.org/upload ---- -name: CI - -on: push - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - macos-latest - - ubuntu-latest - ghc: - - "9.0" - - "8.10" - - steps: - - uses: actions/checkout@v3 - - if: ${{ runner.os == 'Linux' }} - run: | - sudo apt-get update - sudo apt-get install zsh - - if: ${{ runner.os == 'macOS' }} - run: | - brew update - brew install zsh - - uses: haskell/actions/setup@v2 - with: - ghc-version: ${{ matrix.ghc }} - cabal-version: "3.6" - id: setup-haskell - - run: cabal configure --enable-tests - - run: cabal freeze - - uses: actions/cache@v3 - with: - key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} - path: | - ${{ steps.setup-haskell.outputs.cabal-store }} - dist-newstyle - restore-keys: ${{ runner.os }}-${{ matrix.ghc }}- - - run: cabal build - - run: cabal test - - run: ./script/haddock - - release: - needs: build - if: github.event_name == 'release' - runs-on: ubuntu-latest - steps: - - name: Generate package ready for Hackage distribution - run: cabal sdist - - - name: Upload package to Hackage - run: | - cabal upload --publish \ - --username '${{ secrets.HACKAGE_USERNAME }}' \ - --password '${{ secrets.HACKAGE_PASSWORD }}' \ - dist-newstyle/sdist/hapistrano-*.tar.gz - - - name: Generate Haddock docs for Hackage distribution - run: cabal v2-haddock --haddock-for-hackage --enable-doc - - - name: Upload Haddock docs to Hackage - run: | - cabal upload -d --publish \ - --username '${{ secrets.HACKAGE_USERNAME }}' \ - --password '${{ secrets.HACKAGE_PASSWORD }}' \ - dist-newstyle/hapistrano-*-docs.tar.gz diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 19e37b3d..00000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,75 +0,0 @@ -# The present workflow was made based on the following references: -# - https://evilmartians.com/chronicles/build-images-on-github-actions-with-docker-layer-caching -# - https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md ---- -name: Docker - -on: push - -concurrency: - group: docker-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - - env: - REGISTRY: ghcr.io - - permissions: - contents: write - packages: write - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Docker - uses: docker/setup-buildx-action@v1 - - uses: actions/cache@v3 - with: - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: ${{ runner.os }}-buildx- - path: /tmp/.buildx-cache - - name: Login to GH container registry - if: startsWith(github.ref, 'refs/tags/') - uses: docker/login-action@v1 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Generate metadata for Docker image - uses: docker/metadata-action@v3 - with: - images: ${{ env.REGISTRY }}/${{ github.repository }} - id: meta - - name: Build and push Docker image - uses: docker/build-push-action@v2 - with: - context: . - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - push: ${{ startsWith(github.ref, 'refs/tags/') }} - load: true - - name: Smoke test - run: docker run ${{ steps.meta.outputs.tags }} --version - - name: Copy static binary from Docker to the host - if: startsWith(github.ref, 'refs/tags/') - run: docker run -v $PWD/bin:/root/bin --entrypoint cp ${{ steps.meta.outputs.tags }} /usr/local/bin/hap /root/bin/hap-${{ github.ref_name }}-linux-x86_64-bin - - name: Create GH release - if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v1 - with: - files: | - bin/hap-* - LICENSE - draft: true - # Temp fix: - # - https://github.com/docker/build-push-action/issues/252 - # - https://github.com/moby/buildkit/issues/1896 - - name: Move cache - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml new file mode 100644 index 00000000..8e4af321 --- /dev/null +++ b/.github/workflows/draft.yml @@ -0,0 +1,60 @@ +# The present workflow was made based on the following references: +# - https://evilmartians.com/chronicles/build-images-on-github-actions-with-docker-layer-caching +# - https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md +# - https://github.com/commercialhaskell/stack/blob/master/.github/workflows/integration-tests.yml +--- +name: Draft + +on: + create: + tags: + - v* + +concurrency: + group: draft-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: ${{ runner.os }}-buildx- + - name: Build Docker image + uses: docker/build-push-action@v2 + with: + context: . + load: true + tags: ${{ github.repository }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + - name: Copy static binary + run: | + docker run \ + --entrypoint cp \ + --volume $PWD/bin:/root/bin \ + ${{ github.repository }} \ + /usr/local/bin/hap \ + /root/bin/hap-${{ github.ref_name }}-linux-x86_64-bin + - name: Create draft release + uses: softprops/action-gh-release@v1 + with: + files: | + bin/hap-* + LICENSE + draft: true + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..16d540b3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,60 @@ +# The present workflow was made based on the following references: +# - https://github.com/tfausak/strive/blob/main/.github/workflows/ci.yaml +# - https://hackage.haskell.org/upload +--- +name: Release + +on: + release: + types: + - published + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: true + +jobs: + ghcr: + uses: ./.github/workflows/reusable-docker.yml + with: + push: true + + hackage: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Haskell tooling + uses: haskell/actions/setup@v2 + with: + ghc-version: "8.10" + cabal-version: "3.6" + - name: Freeze dependencies + run: cabal freeze + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cabal/package + ~/.cabal/store + dist-newstyle + key: ${{ runner.os }}-${{ hashFiles('cabal.project.freeze') }} + restore-keys: ${{ runner.os }}- + - name: Generate a source distribution file + run: cabal sdist + - name: Upload package to Hackage + run: | + cabal upload --publish \ + --username "${{ secrets.HACKAGE_USERNAME }}" \ + --password "${{ secrets.HACKAGE_PASSWORD }}" \ + dist-newstyle/sdist/hapistrano-*.tar.gz || \ + echo "Already exists" + - name: Build Haddock documentation + run: cabal v2-haddock --haddock-for-hackage --enable-documentation + - name: Upload Haddock docs to Hackage + run: | + cabal upload --publish \ + --username "${{ secrets.HACKAGE_USERNAME }}" \ + --password "${{ secrets.HACKAGE_PASSWORD }}" \ + --documentation \ + dist-newstyle/hapistrano-*-docs.tar.gz diff --git a/.github/workflows/reusable-docker.yml b/.github/workflows/reusable-docker.yml new file mode 100644 index 00000000..b119ba80 --- /dev/null +++ b/.github/workflows/reusable-docker.yml @@ -0,0 +1,56 @@ +--- +name: Docker + +on: + workflow_call: + inputs: + push: + type: boolean + required: true + +jobs: + build: + runs-on: ubuntu-latest + permissions: + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: ${{ runner.os }}-buildx- + - name: Login to ghcr.io + if: ${{ inputs.push }} + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ghcr.io/${{ github.repository }} + - name: Build and/or push Docker image + uses: docker/build-push-action@v3 + with: + context: . + load: ${{ !inputs.push }} + push: ${{ inputs.push }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + - name: Smoke test + if: ${{ !inputs.push }} + run: docker run ${{ steps.meta.outputs.tags }} --version + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/README.md b/README.md index d4f87b62..b9566869 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![CI](https://github.com/stackbuilders/hapistrano/actions/workflows/ci.yml/badge.svg)](https://github.com/stackbuilders/hapistrano/actions/workflows/ci.yml) -[![Docker](https://github.com/stackbuilders/hapistrano/actions/workflows/docker.yml/badge.svg)](https://github.com/stackbuilders/hapistrano/actions/workflows/docker.yml) -[![Hackage version](https://img.shields.io/hackage/v/hapistrano.svg)](http://hackage.haskell.org/package/hapistrano) +[![Build](https://github.com/stackbuilders/hapistrano/actions/workflows/build.yml/badge.svg)](https://github.com/stackbuilders/hapistrano/actions/workflows/build.yml) +[![Draft](https://github.com/stackbuilders/hapistrano/actions/workflows/draft.yml/badge.svg)](https://github.com/stackbuilders/hapistrano/actions/workflows/draft.yml) +[![Release](https://github.com/stackbuilders/hapistrano/actions/workflows/release.yml/badge.svg)](https://github.com/stackbuilders/hapistrano/actions/workflows/release.yml) # Hapistrano @@ -233,6 +233,10 @@ Hub](https://hub.docker.com/r/stackbuilders/hapistrano) are **no longer up to date**, newer versions are published to [GitHub's Docker Registry](https://github.com/stackbuilders/hapistrano/pkgs/container/hapistrano). +## GH Actions + +Check the documentation [here](.github/workflows/README.md) + ## Nix If you want to use Nix for building Hapistrano, the required release.nix and default.nix are available.