name: Validate # We use bash as default even in windows # to try keep the workflow as uniform as possible defaults: run: shell: bash # See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency. concurrency: group: ${{ github.ref }}-${{ github.workflow }} cancel-in-progress: true on: push: branches: - master pull_request: release: types: - created env: # We choose a stable ghc version across all os's # which will be used to do the next release GHC_FOR_RELEASE: '9.2.8' # Ideally we should use the version about to be released for hackage tests and benchmarks GHC_FOR_SOLVER_BENCHMARKS: '9.2.8' GHC_FOR_COMPLETE_HACKAGE_TESTS: '9.2.8' COMMON_FLAGS: '-j 2 -v' jobs: validate: name: Validate ${{ matrix.os }} ghc-${{ matrix.ghc }} runs-on: ${{ matrix.os }} outputs: GHC_FOR_RELEASE: ${{ format('["{0}"]', env.GHC_FOR_RELEASE) }} strategy: matrix: os: ["ubuntu-20.04", "macos-latest", "windows-latest"] ghc: ["9.4.8", "9.2.8", "9.0.2", "8.10.7", "8.8.4", "8.6.5", "8.4.4"] exclude: # Run takes 4+ hours - os: "windows-latest" ghc: "9.0.2" # corrupts GHA cache or the fabric of reality itself, see https://github.com/haskell/cabal/issues/8356 - os: "windows-latest" ghc: "8.10.7" # lot of segfaults caused by ghc bugs - os: "windows-latest" ghc: "8.8.4" # it also throws segfaults randomly - os: "windows-latest" ghc: "8.4.4" # it often randomly does "C:\Users\RUNNER~1\AppData\Local\Temp\ghcFEDE.c: DeleteFile "\\\\?\\C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\ghcFEDE.c": permission denied (Access is denied.)" - os: "windows-latest" ghc: "8.6.5" steps: - uses: actions/checkout@v3 # See https://github.com/haskell/cabal/pull/8739 - name: Sudo chmod to permit ghcup to update its cache run: | if [[ "${{ runner.os }}" == "Linux" ]]; then sudo ls -lah /usr/local/.ghcup/cache sudo mkdir -p /usr/local/.ghcup/cache sudo ls -lah /usr/local/.ghcup/cache sudo chown -R $USER /usr/local/.ghcup sudo chmod -R 777 /usr/local/.ghcup fi - uses: haskell-actions/setup@v2 id: setup-haskell with: ghc-version: ${{ matrix.ghc }} cabal-version: '3.10.1.0' # See the following link for a breakdown of the following step # https://github.com/haskell/actions/issues/7#issuecomment-745697160 # # See https://github.com/haskell/cabal/pull/8739 for why Windows is excluded - if: ${{ runner.os != 'Windows' }} uses: actions/cache@v3 with: # validate.sh uses a special build dir path: | ${{ steps.setup-haskell.outputs.cabal-store }} dist-* key: ${{ runner.os }}-${{ matrix.ghc }}-20220419-${{ github.sha }} restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-20220419- - name: Work around git problem https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586 (cabal PR #8546) run: | git config --global protocol.file.allow always # The '+exe' constraint below is important, otherwise cabal-install # might decide to build the library but not the executable which is # what we need. - name: Install cabal-plan run: | cd $(mktemp -d) cabal install cabal-plan --constraint='cabal-plan +exe' echo "$HOME/.cabal/bin" >> $GITHUB_PATH # The tool is not essential to the rest of the test suite. If # hackage-repo-tool is not present, any test that requires it will # be skipped. # We want to keep this in the loop but we don't want to fail if # hackage-repo-tool breaks or fails to support a newer GHC version. - name: Install hackage-repo-tool continue-on-error: true run: | cd $(mktemp -d) cabal install hackage-repo-tool # Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs - name: Install Autotools if: runner.os == 'macOS' run: | brew install automake - name: Set validate inputs run: | FLAGS="${{ env.COMMON_FLAGS }}" if [[ "${{ matrix.cli }}" == "false" ]]; then FLAGS="$FLAGS --lib-only" fi if [[ ${{ matrix.ghc }} == ${{ env.GHC_FOR_SOLVER_BENCHMARKS }} ]]; then FLAGS="$FLAGS --solver-benchmarks" fi if [[ ${{ matrix.ghc }} == ${{ env.GHC_FOR_COMPLETE_HACKAGE_TESTS }} ]]; then FLAGS="$FLAGS --complete-hackage-tests" fi echo "FLAGS=$FLAGS" >> $GITHUB_ENV - name: Validate print-config run: sh validate.sh $FLAGS -s print-config - name: Validate print-tool-versions run: sh validate.sh $FLAGS -s print-tool-versions - name: Validate build run: sh validate.sh $FLAGS -s build - name: Tar cabal head executable if: matrix.cli != 'false' && matrix.ghc == env.GHC_FOR_RELEASE run: | CABAL_EXEC=$(cabal-plan list-bin --builddir=dist-newstyle-validate-ghc-${{ matrix.ghc }} cabal-install:exe:cabal) # We have to tar the executable to preserve executable permissions # see https://github.com/actions/upload-artifact/issues/38 if [[ ${{ runner.os }} == 'Windows' ]]; then # `cabal-plan` gives us a windows path but tar needs the posix one CABAL_EXEC=$(cygpath $CABAL_EXEC) fi if [[ "${{ runner.os }}" == "macOS" ]]; then # Workaround to avoid bsdtar corrupts the executable # so executing it after untar throws `cannot execute binary file` # see https://github.com/actions/virtual-environments/issues/2619#issuecomment-788397841 sudo /usr/sbin/purge fi tar -cvf cabal-head.tar -C $(dirname "$CABAL_EXEC") $(basename "$CABAL_EXEC") echo "CABAL_EXEC_TAR=cabal-head.tar" >> $GITHUB_ENV # We upload the cabal executable built with the ghc used in the release for: # - Reuse it in the dogfooding job (although we could use the cached build dir) # - Make it available in the workflow to make easier testing it locally - name: Upload cabal-install executable to workflow artifacts if: matrix.cli != 'false' && matrix.ghc == env.GHC_FOR_RELEASE uses: actions/upload-artifact@v3 with: name: cabal-${{ runner.os }}-${{ matrix.ghc }} path: ${{ env.CABAL_EXEC_TAR }} - name: Validate lib-tests env: # `rawSystemStdInOut reports text decoding errors` # test does not find ghc without the full path in windows GHCPATH: ${{ steps.setup-haskell.outputs.ghc-exe }} run: sh validate.sh $FLAGS -s lib-tests - name: Validate lib-suite run: sh validate.sh $FLAGS -s lib-suite - name: Validate cli-tests if: matrix.cli != 'false' run: sh validate.sh $FLAGS -s cli-tests - name: Validate cli-suite if: matrix.cli != 'false' run: sh validate.sh $FLAGS -s cli-suite # The job below is a copy-paste of validate with the necessary tweaks # to make all work with an upcoming GHC. Those tweaks include: # - ghcup needs the prerelease channel activated # - allow-newer for base libraries and Cabal* libraries # - (sometimes) disabling some parts on Windows because it's hard to figure # out why they fail validate-prerelease: # TODO: reenable when the next GHC prerelease appears if: false name: Validate ${{ matrix.os }} ghc-prerelease runs-on: ${{ matrix.os }} outputs: GHC_FOR_RELEASE: ${{ format('["{0}"]', env.GHC_FOR_RELEASE) }} strategy: matrix: os: ["ubuntu-20.04", "macos-latest", "windows-latest"] steps: - uses: actions/checkout@v3 # See https://github.com/haskell/cabal/pull/8739 - name: Sudo chmod to permit ghcup to update its cache run: | if [[ "${{ runner.os }}" == "Linux" ]]; then sudo ls -lah /usr/local/.ghcup/cache sudo mkdir -p /usr/local/.ghcup/cache sudo ls -lah /usr/local/.ghcup/cache sudo chown -R $USER /usr/local/.ghcup sudo chmod -R 777 /usr/local/.ghcup fi - name: ghcup run: | ghcup --version ghcup config set cache true ghcup config add-release-channel https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.7.yaml ghcup install ghc --set 9.6.0.20230210 ghcup install cabal --set latest ghc --version cabal update # See the following link for a breakdown of the following step # https://github.com/haskell/actions/issues/7#issuecomment-745697160 # # See https://github.com/haskell/cabal/pull/8739 for why Windows is excluded - if: ${{ runner.os != 'Windows' }} uses: actions/cache@v3 with: # validate.sh uses a special build dir path: | ${{ steps.setup-haskell.outputs.cabal-store }} dist-* key: ${{ runner.os }}-${{ matrix.ghc }}-20220419-${{ github.sha }} restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-20220419- - name: Work around git problem https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586 (cabal PR #8546) run: | git config --global protocol.file.allow always # The '+exe' constraint below is important, otherwise cabal-install # might decide to build the library but not the executable which is # what we need. - name: Install cabal-plan run: | cd $(mktemp -d) cabal install cabal-plan --constraint='cabal-plan +exe' --allow-newer echo "$HOME/.cabal/bin" >> $GITHUB_PATH # The tool is not essential to the rest of the test suite. If # hackage-repo-tool is not present, any test that requires it will # be skipped. # We want to keep this in the loop but we don't want to fail if # hackage-repo-tool breaks or fails to support a newer GHC version. - name: Install hackage-repo-tool continue-on-error: true run: | cd $(mktemp -d) cabal install hackage-repo-tool # Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs - name: Install Autotools if: runner.os == 'macOS' run: | brew install automake - name: Allow newer boot libraries run: | echo "allow-newer: base, template-haskell, ghc-prim, Cabal-syntax, Cabal-described, Cabal, cabal-install-solver, cabal-install" >> cabal.project.validate - name: Set validate inputs run: | FLAGS="${{ env.COMMON_FLAGS }}" if [[ "${{ matrix.cli }}" == "false" ]]; then FLAGS="$FLAGS --lib-only" fi echo "FLAGS=$FLAGS" >> $GITHUB_ENV - name: Validate print-config run: sh validate.sh $FLAGS -s print-config - name: Validate print-tool-versions run: sh validate.sh $FLAGS -s print-tool-versions - name: Validate build run: sh validate.sh $FLAGS -s build - name: Validate lib-tests env: # `rawSystemStdInOut reports text decoding errors` # test does not find ghc without the full path in windows GHCPATH: ${{ steps.setup-haskell.outputs.ghc-exe }} run: sh validate.sh $FLAGS -s lib-tests - name: Validate lib-suite # see https://github.com/haskell/cabal/pull/8754#issuecomment-1435025848 # for discussion about the trouble on Windows if: ${{ runner.os != 'Windows' }} run: sh validate.sh $FLAGS -s lib-suite - name: Validate cli-tests if: matrix.cli != 'false' run: sh validate.sh $FLAGS -s cli-tests - name: Validate cli-suite # see https://github.com/haskell/cabal/pull/8754#issuecomment-1435025848 # for discussion about the trouble on Windows if: ( runner.os != 'Windows' ) && ( matrix.cli != 'false' ) run: sh validate.sh $FLAGS -s cli-suite validate-old-ghcs: name: Validate old ghcs ${{ matrix.extra-ghc }} runs-on: ubuntu-20.04 needs: validate # This job needs an older ubuntu (16.04) cause # the required old ghcs using the `-dyn` flavour # are not installable from ppa/hvr in newer ones # see https://github.com/haskell/cabal/issues/8011 container: image: phadej/ghc:8.8.4-xenial strategy: matrix: # Newer ghc versions than 8.8.4 have to be installed with ghcup cause # they are not available in ppa/hvr. The ghcup installation # needs `sudo` which is not available in the xenial container ghc: ["8.8.4"] extra-ghc: ["7.10.3", "7.8.4", "7.6.3", "7.4.2", "7.2.2", "7.0.4"] steps: # We can't use actions/checkout with the xenial docker container # cause it does not work with the git version included in it, see: # https://github.com/actions/checkout/issues/170 # https://github.com/actions/checkout/issues/295 # - uses: actions/checkout@v3 - name: Checkout run: | echo $GITHUB_REF $GITHUB_SHA git clone --depth 1 https://github.com/$GITHUB_REPOSITORY.git . git fetch origin $GITHUB_SHA:temporary-ci-branch git checkout $GITHUB_SHA || (git fetch && git checkout $GITHUB_SHA) - name: Install extra compiler run: | apt-get update apt-get install -y ghc-${{ matrix.extra-ghc }}-dyn - uses: haskell-actions/setup@v2 id: setup-haskell with: ghc-version: ${{ matrix.ghc }} # Make sure this bindist works in this old environment cabal-version: 3.10.1.0 # As we are reusing the cached build dir from the previous step # the generated artifacts are available here, # including the cabal executable and the test suite - uses: actions/cache@v3 with: path: | ${{ steps.setup-haskell.outputs.cabal-store }} dist-* key: ${{ runner.os }}-${{ matrix.ghc }}-20220419-${{ github.sha }} restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-20220419- - name: Install cabal-plan run: | cd $(mktemp -d) cabal install cabal-plan --constraint='cabal-plan +exe' echo "$HOME/.cabal/bin" >> $GITHUB_PATH - name: Validate build run: sh validate.sh ${{ env.COMMON_FLAGS }} -s build - name: "Validate lib-suite-extras --extra-hc ghc-${{ matrix.extra-ghc }}" env: EXTRA_GHC: "/opt/ghc/${{ matrix.extra-ghc }}/bin/ghc-${{ matrix.extra-ghc }}" run: sh validate.sh ${{ env.COMMON_FLAGS }} --lib-only -s lib-suite-extras --extra-hc ${{ env.EXTRA_GHC }} # The previous jobs use a released version of cabal to build cabal HEAD itself # This one uses the cabal HEAD generated executable in the previous step # to build itself again, as sanity check dogfooding: name: Dogfooding ${{ matrix.os }} ghc-${{ matrix.ghc }} runs-on: ${{ matrix.os }} needs: validate strategy: matrix: os: ["ubuntu-20.04", "macos-latest", "windows-latest"] # We only use one ghc version the used one for the next release (defined at top of the workflow) # We need to build an array dynamically to inject the appropiate env var in a previous job, # see https://docs.github.com/en/actions/learn-github-actions/expressions#fromjson ghc: ${{ fromJSON (needs.validate.outputs.GHC_FOR_RELEASE) }} steps: - uses: actions/checkout@v3 # See https://github.com/haskell/cabal/pull/8739 - name: Sudo chmod to permit ghcup to update its cache run: | if [[ "${{ runner.os }}" == "Linux" ]]; then sudo ls -lah /usr/local/.ghcup/cache sudo mkdir -p /usr/local/.ghcup/cache sudo ls -lah /usr/local/.ghcup/cache sudo chown -R $USER /usr/local/.ghcup sudo chmod -R 777 /usr/local/.ghcup fi - uses: haskell-actions/setup@v2 id: setup-haskell with: ghc-version: ${{ matrix.ghc }} cabal-version: latest # default, we are not using it in this job - name: Install cabal-plan run: | cd $(mktemp -d) cabal install cabal-plan --constraint='cabal-plan +exe' echo "$HOME/.cabal/bin" >> $GITHUB_PATH - name: Download cabal executable from workflow artifacts uses: actions/download-artifact@v3 with: name: cabal-${{ runner.os }}-${{ matrix.ghc }} path: cabal-head - name: Untar the cabal executable run: tar -xf ./cabal-head/cabal-head.tar -C ./cabal-head - name: print-config using cabal HEAD run: sh validate.sh ${{ env.COMMON_FLAGS }} --with-cabal ./cabal-head/cabal -s print-config # We dont use cache to force a build with a fresh store dir and build dir # This way we check cabal can build all its dependencies - name: Build using cabal HEAD run: sh validate.sh ${{ env.COMMON_FLAGS }} --with-cabal ./cabal-head/cabal -s build # We use this job as a summary of the workflow # It will fail if any of the previous jobs does it # This way we can use it exclusively in branch protection rules # and abstract away the concrete jobs of the workflow, including their names validate-post-job: if: always() name: Validate post job runs-on: ubuntu-20.04 # IMPORTANT! Any job added to the workflow should be added here too needs: [validate, validate-old-ghcs, dogfooding] steps: - run: | echo "jobs info: ${{ toJSON(needs) }}" - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') run: exit 1