# Runs on notable pushes to ci-linux, on notable pull requests against
# master, and weekly on master.

# Builds all modules optimised and runs
# unit/doc/functional/haddock/benchmark tests on linux,
# using the oldest supported GHC version (only, as a compromise to
# reduce carbon emissions. Some of the other versions are exercised by
# the other workflows.)
# Currently does not upload a binaries artifact.

# This is the "master" workflow on which the others are based, 
# with the most detailed notes.

name: linux CI

on:
  # Scheduled workflows run on the latest commit on the default or base branch. (master)
  schedule:
  - cron: "0 07 * * 0"  # sunday midnight pacific

  pull_request:
    branches: [ master ]
    paths:
      - '.github/workflows/linux.yml'
      - 'stack*.yaml'
      - 'hledger-lib/**'
      - 'hledger/**'
      - 'hledger-ui/**'
      - 'hledger-web/**'
      - 'bin/*.hs'
      # ignore changes to example files, though currently some func tests depend on them
      - 'examples/**'
      - '!**.journal'
      - '!**.j'
      - '!**.ledger'
      - '!**.csv'
      # ignore changes to doc source files
      - '!**.m4'
      - '!**.md'
      - '!**.1'
      - '!**.5'
      - '!**.info'
      - '!**.txt'

  push:
    branches: [ ci-linux ]
    paths:
      - '.github/workflows/linux.yml'
      - 'stack*'
      - 'hledger-lib/**'
      - 'hledger/**'
      - 'hledger-ui/**'
      - 'hledger-web/**'
      # ignore changes to example files, though currently some func tests depend on them
      - 'examples/**'
      - '!**.journal'
      - '!**.j'
      - '!**.ledger'
      - '!**.csv'
      # ignore changes to doc source files
      - '!**.m4'
      - '!**.md'
      - '!**.1'
      - '!**.5'
      - '!**.info'
      - '!**.txt'

  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        plan:
        - { ghc: "86"  , stack: "stack --stack-yaml=stack8.6.yaml" }
        # - { ghc: "88"  , stack: "stack --stack-yaml=stack8.8.yaml" }
        # - { ghc: "810" , stack: "stack --stack-yaml=stack8.10.yaml" }
        # - { ghc: "90" , stack: "stack --stack-yaml=stack.yaml" }

    steps:

    - name: Check out
      uses: actions/checkout@v2
      # have to fetch everything for git describe for --version
      with: 
        fetch-depth: 0        

    # - name: Print debug output
    #   env:
    #     GITHUB_CONTEXT: ${{ toJson(github) }}
    #   run: |
    #     echo

    #     echo "$GITHUB_SHA"
    #     echo "$GITHUB_REF"
    #     echo "$GITHUB_HEAD_REF"
    #     echo "$GITHUB_BASE_REF"
    #     git log "$GITHUB_BASE_REF"..
    #     bin/commitlint "$GITHUB_BASE_REF"..

    # keep synced in all workflows which do this
    - name: Check commit messages
      # For a PR, the range will be: master..origin/$GITHUB_HEAD_REF
      # For a push it will be: $BEFORE..
      # For a force push, BEFORE is the previous HEAD, and on github (though not locally) this is an "invalid revision range".
      # In this and any case where the range is invalid, we'll just check the last N commits.
      # related: https://stackoverflow.com/questions/64708371/how-to-run-github-workflow-on-every-commit-of-a-push
      env:
        BEFORE: ${{ github.event.before }}
        NUM:    20
      run: |
        RANGE=${BEFORE:-origin/master}..${GITHUB_HEAD_REF:+origin/$GITHUB_HEAD_REF}
        git rev-list --quiet $RANGE \
          && bin/commitlint  $RANGE \
          || ( echo "could not identify commits, checking last $NUM instead:"; bin/commitlint -$NUM )

    - name: Skip remaining CI steps if latest commit message begins with ;
      run: |
        git log -1 --pretty='%s' ${GITHUB_HEAD_REF:+origin/$GITHUB_HEAD_REF} \
        | grep -qE '^ *;' || echo "CONTINUE=true" >> $GITHUB_ENV 

    # things to be cached/restored:

    - name: Cache stack global package db
      id:   stack-global
      uses: actions/cache@v2
      with:
        path: ~/.stack
        key: ${{ runner.os }}-stack-global-${{ matrix.plan.ghc }}-${{ hashFiles('**.yaml') }}
        restore-keys: |
             ${{ runner.os }}-stack-global-${{ matrix.plan.ghc }}
      if: env.CONTINUE

    - name: Cache stack-installed programs in ~/.local/bin
      id:   stack-programs
      uses: actions/cache@v2
      with:
        path: ~/.local/bin
        key: ${{ runner.os }}-stack-programs-${{ matrix.plan.ghc }}-${{ hashFiles('**.yaml') }}
        restore-keys: |
             ${{ runner.os }}-stack-programs-${{ matrix.plan.ghc }}
      if: env.CONTINUE

    - name: Cache .stack-work
      uses: actions/cache@v2
      with:
        path: .stack-work
        key: ${{ runner.os }}-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('**.yaml') }}
        restore-keys: |
             ${{ runner.os }}-stack-work-${{ matrix.plan.ghc }}
      if: env.CONTINUE

    - name: Cache hledger-lib/.stack-work
      uses: actions/cache@v2
      with:
        path: hledger-lib/.stack-work
        key: ${{ runner.os }}-hledger-lib-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('hledger-lib/package.yaml') }}
        restore-keys: |
             ${{ runner.os }}-hledger-lib-stack-work-${{ matrix.plan.ghc }}
      if: env.CONTINUE

    - name: Cache hledger/.stack-work
      uses: actions/cache@v2
      with:
        path: hledger/.stack-work
        key: ${{ runner.os }}-hledger-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('hledger/package.yaml') }}
        restore-keys: |
             ${{ runner.os }}-hledger-stack-work-${{ matrix.plan.ghc }}
      if: env.CONTINUE

    - name: Cache hledger-ui/.stack-work
      uses: actions/cache@v2
      with:
        path: hledger-ui/.stack-work
        key: ${{ runner.os }}-hledger-ui-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('hledger-ui/package.yaml') }}
        restore-keys: |
             ${{ runner.os }}-hledger-ui-stack-work-${{ matrix.plan.ghc }}
      if: env.CONTINUE

    - name: Cache hledger-web/.stack-work
      uses: actions/cache@v2
      with:
        path: hledger-web/.stack-work
        key: ${{ runner.os }}-hledger-web-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('hledger-web/package.yaml') }}
        restore-keys: |
             ${{ runner.os }}-hledger-web-stack-work-${{ matrix.plan.ghc }}
      if: env.CONTINUE

    # no longer needed I think
    # For now due to
    # https://github.com/actions/virtual-environments/issues/709
    # there's only 4.8G free at this point, and we must free up space.
    # - name: Free disk space
    #   run: |
    #     df -h /
    #     # 8G:
    #     sudo swapoff -a
    #     sudo rm -f /swapfile
    #     # 3G:
    #     sudo apt clean
    #     # 3G, takes 30s:
    #     # docker image ls -aq
    #     # docker rmi $(docker image ls -aq)
    #     # 1G:
    #     find ~/work/_temp -name "cache.tgz" -exec rm -f {} \;
    #     # 4G, takes 14s:
    #     # sudo rm -rf "/usr/local/share/boost"
    #     # 2G:
    #     sudo rm -rf "$AGENT_TOOLSDIRECTORY"
    #     df -h /

    # actions:

    - name: Install stack
      run: |
        mkdir -p ~/.local/bin
        export PATH=~/.local/bin:$PATH
        # curl -sL https://get.haskellstack.org/stable/linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'; chmod a+x ~/.local/bin/stack
        if [[ ! -x ~/.local/bin/stack ]]; then curl -sL https://get.haskellstack.org/stable/linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'; chmod a+x ~/.local/bin/stack; fi
        stack --version
      if: env.CONTINUE

    - name: Install GHC
      env:
        stack: ${{ matrix.plan.stack }}
      run: |
        df -h
        $stack setup --install-ghc
        df -h
      if: env.CONTINUE

    - name: Install haskell deps
      env:
        stack: ${{ matrix.plan.stack }}
      run: |
        $stack build --test --bench --only-dependencies
        #  --no-terminal
      if: env.CONTINUE

    - name: Build all hledger modules warning free, optimised and minimised, run unit/doc/bench tests
      env:
        stack: ${{ matrix.plan.stack }}
      run: |
        $stack install --test --bench --force-dirty --ghc-options=-fforce-recomp --ghc-options=-Werror --ghc-options=-split-sections --no-terminal
        # build quicker when tweaking ci: $stack install --ghc-options=-Werror --ghc-options=-split-sections --no-terminal
        # -split-sections shrinks binaries by 30% on average here
        # --pedantic --no-run-benchmarks
      if: env.CONTINUE

    - name: Install shelltestrunner
      env:
        stack: ${{ matrix.plan.stack }}
      run: |
        export PATH=~/.local/bin:$PATH
        if [[ ! -x ~/.local/bin/shelltest ]]; then $stack install shelltestrunner-1.9; fi
        shelltest --version
      if: env.CONTINUE

    - name: Test functional tests (excluding addons)
      env:
        stack: ${{ matrix.plan.stack }}
      run: |
        export PATH=~/.local/bin:$PATH
        COLUMNS=80 $stack exec -- shelltest --execdir -j16 hledger/test -x /_ -x /addons
        # XXX run the bin/ func tests corresponding to the GHC version enabled above, only
      if: env.CONTINUE

    - name: Test haddock generation
      env:
        stack: ${{ matrix.plan.stack }}
      run: |
        time $stack build --haddock --no-haddock-deps --no-haddock-hyperlink-source --haddock-arguments="--no-print-missing-docs"
        # --no-haddock-hyperlink-source is 25% faster
        # --no-print-missing-docs is 600% quieter
      if: env.CONTINUE

    # artifacts:
    # XXX unreliable, and we don't need this for every PR; disable for now

    - name: Gather executables
      id: exes
      run: |
        mkdir tmp
        cd tmp
        mkdir hledger
        cd hledger
        cp ~/.local/bin/hledger .
        cp ~/.local/bin/hledger-ui .
        cp ~/.local/bin/hledger-web .
    # example of setting a context variable, and an attempt to make a nice artifact version suffix.
    # But a constant name is easier in some ways.
    #   echo "::set-output name=version::$(git branch --show-current | sed 's/-.*//')-$(git rev-parse --short HEAD)"
      if: env.CONTINUE

    # XXX intermittent upload failures
    - name: Upload executables artifact
      uses: actions/upload-artifact@v2
      with:
    #    name: hledger-ubuntu-${{ steps.exes.outputs.version }}
        name: hledger-ubuntu
        path: tmp/hledger
      if: env.CONTINUE





    # - name: show stuff
    #   run: |
    #     if [[ -e ~/.local/bin ]]; then ls -lFRa ~/.local/bin; fi

    # inspect available context info, per
    # https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions.
    # sample output: https://github.com/simonmichael/hledger/runs/1619227104
    # - name: Dump GitHub context
    #   env:
    #     GITHUB_CONTEXT: ${{ toJson(github) }}
    #   run: echo "$GITHUB_CONTEXT"
    # - name: Dump job context
    #   env:
    #     JOB_CONTEXT: ${{ toJson(job) }}
    #   run: echo "$JOB_CONTEXT"
    # - name: Dump steps context
    #   env:
    #     STEPS_CONTEXT: ${{ toJson(steps) }}
    #   run: echo "$STEPS_CONTEXT"
    # - name: Dump runner context
    #   env:
    #     RUNNER_CONTEXT: ${{ toJson(runner) }}
    #   run: echo "$RUNNER_CONTEXT"
    # - name: Dump strategy context
    #   env:
    #     STRATEGY_CONTEXT: ${{ toJson(strategy) }}
    #   run: echo "$STRATEGY_CONTEXT"
    # - name: Dump matrix context
    #   env:
    #     MATRIX_CONTEXT: ${{ toJson(matrix) }}
    #   run: echo "$MATRIX_CONTEXT"



# docs:

# based on https://gist.github.com/mstksg/11f753d891cee5980326a8ea8c865233
# http://www.btellez.com/posts/triggering-github-actions-with-webhooks.html
# https://help.github.com/en/actions
# https://help.github.com/en/actions/reference/events-that-trigger-workflows
# https://help.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events-schedule
# https://help.github.com/en/actions/configuring-and-managing-workflows/caching-and-storing-workflow-data
# https://help.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows
# https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources
# https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
# https://github.community/t5/GitHub-Actions/bd-p/actions

# https://github.com/actions/cache
# https://github.com/marketplace/actions/cache

# https://github.com/actions/upload-artifact
# https://github.com/c-hive/gha-remove-artifacts

# https://github.com/actions/setup-haskell

# https://github.com/marketplace/actions/checkout
# https://github.com/sdras/awesome-actions

# https://sevenzip.osdn.jp/chm/cmdline/commands/index.htm

# https://github.com/mxschmitt/action-tmate
#     - name: Setup tmate session
#      uses: mxschmitt/action-tmate@v1
