CI: dogfooding should run on the same Mac runner as validate (#10101)
[cabal.git] / .github / workflows / validate.yml
bloba34023d2d9e6e4cbae5ead1be7f08d84041a4c93
1 name: Validate
3 # We use bash as default even in windows
4 # to try keep the workflow as uniform as possible
5 defaults:
6   run:
7     shell: bash
9 # See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency.
10 concurrency:
11   group: ${{ github.ref }}-${{ github.workflow }}
12   cancel-in-progress: true
14 # Note: This workflow file contains the required job "Validate post job". We are using path filtering
15 # here to ignore PRs which only change documentation. This can cause a problem, see the workflow file
16 # "validate.skip.yml" for a description of the problem and the solution provided in that file.
17 on:
18   push:
19     paths-ignore:
20       - 'doc/**'
21       - '**/README.md'
22       - 'CONTRIBUTING.md'
23     branches:
24       - master
25   pull_request:
26     paths-ignore:
27       - 'doc/**'
28       - '**/README.md'
29       - 'CONTRIBUTING.md'
30   release:
31     types:
32       - created
33   workflow_call:
35   # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
36   workflow_dispatch:
37     inputs:
38       allow-newer:
39         description: allow-newer line
40         required: true
41         type: string
42       constraints:
43         description: constraints line
44         required: true
45         type: string
47 env:
48   # We choose a stable ghc version across all os's
49   # which will be used to do the next release
50   GHC_FOR_RELEASE: '9.4.8'
51   # Ideally we should use the version about to be released for hackage tests and benchmarks
52   GHC_FOR_SOLVER_BENCHMARKS: '9.4.8'
53   GHC_FOR_COMPLETE_HACKAGE_TESTS: '9.4.8'
54   COMMON_FLAGS: '-j 2 -v'
56   # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
57   ALLOWNEWER: ${{ github.event.inputs.allow-newer }}
58   CONSTRAINTS: ${{ github.event.inputs.constraints }}
61 jobs:
62   validate:
63     name: Validate ${{ matrix.os }} ghc-${{ matrix.ghc }}
64     runs-on: ${{ matrix.os }}
65     outputs:
66       GHC_FOR_RELEASE: ${{ format('["{0}"]', env.GHC_FOR_RELEASE) }}
67     strategy:
68       matrix:
69         os: [ubuntu-latest, macos-13, windows-latest]
70         # If you remove something from here, then add it to the old-ghcs job.
71         # Also a removed GHC from here means that we are actually dropping
72         # support, so the PR *must* have a changelog entry.
73         ghc: ['9.10.1', '9.8.2', '9.6.4', '9.4.8', '9.2.8', '9.0.2', '8.10.7', '8.8.4', '8.6.5']
74         exclude:
75           # corrupts GHA cache or the fabric of reality itself, see https://github.com/haskell/cabal/issues/8356
76           - os: windows-latest
77             ghc: '8.10.7'
78           # lot of segfaults caused by ghc bugs
79           - os: windows-latest
80             ghc: '8.8.4'
81           # 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.)"
82           - os: windows-latest
83             ghc: '8.6.5'
85     steps:
87       - name: Work around XDG directories existence (haskell-actions/setup#62)
88         if: runner.os == 'macOS'
89         run: |
90           rm -rf ~/.config/cabal
91           rm -rf ~/.cache/cabal
93       - uses: actions/checkout@v4
95       # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
96       - name: Manually supplied constraints/allow-newer
97         if: github.event_name == 'workflow_dispatch'
98         run: |
99           echo "allow-newer: ${ALLOWNEWER}"  >> cabal.validate.project
100           echo "constraints: ${CONSTRAINTS}" >> cabal.validate.project
102       - uses: haskell-actions/setup@v2
103         id: setup-haskell
104         with:
105           ghc-version: ${{ matrix.ghc }}
106           cabal-version: latest # latest is mandatory for cabal-testsuite, see https://github.com/haskell/cabal/issues/8133
107           ghcup-release-channel: https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.8.yaml
109       #  See the following link for a breakdown of the following step
110       #  https://github.com/haskell/actions/issues/7#issuecomment-745697160
111       - uses: actions/cache@v4
112         with:
113           # validate.sh uses a special build dir
114           path: |
115             ${{ steps.setup-haskell.outputs.cabal-store }}
116             dist-*
117           key: ${{ runner.os }}-${{ matrix.ghc }}-${{ github.sha }}
118           restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-
120       - name: Work around git problem https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586 (cabal PR #8546)
121         run: git config --global protocol.file.allow always
123       # The tool is not essential to the rest of the test suite. If
124       # hackage-repo-tool is not present, any test that requires it will
125       # be skipped.
126       # We want to keep this in the loop but we don't want to fail if
127       # hackage-repo-tool breaks or fails to support a newer GHC version.
128       - name: Install hackage-repo-tool
129         continue-on-error: true
130         run: cabal install --ignore-project hackage-repo-tool
132       # Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs
133       - name: Install Autotools
134         if: runner.os == 'macOS'
135         run: brew install automake
137       - name: Set validate inputs
138         run: |
139           FLAGS="${{ env.COMMON_FLAGS }}"
140           if [[ "${{ matrix.ghc }}" == "${{ env.GHC_FOR_SOLVER_BENCHMARKS }}" ]]; then
141             FLAGS="$FLAGS --solver-benchmarks"
142           fi
143           if [[ "${{ matrix.ghc }}" == "${{ env.GHC_FOR_COMPLETE_HACKAGE_TESTS }}" ]]; then
144             FLAGS="$FLAGS --complete-hackage-tests"
145           fi
146           echo "FLAGS=$FLAGS" >> "$GITHUB_ENV"
148       - name: Validate print-config
149         run: sh validate.sh $FLAGS -s print-config
151       - name: Validate print-tool-versions
152         run: sh validate.sh $FLAGS -s print-tool-versions
154       - name: Validate build
155         run: sh validate.sh $FLAGS -s build
157       - name: Tar cabal head executable
158         if: matrix.ghc == env.GHC_FOR_RELEASE
159         run: |
160           CABAL_EXEC=$(cabal list-bin --builddir=dist-newstyle-validate-ghc-${{ matrix.ghc }} --project-file=cabal.validate.project cabal-install:exe:cabal)
161           # We have to tar the executable to preserve executable permissions
162           # see https://github.com/actions/upload-artifact/issues/38
163           if [[ "${{ runner.os }}" == "Windows" ]]; then
164             # `cabal list-bin` gives us a windows path but tar needs the posix one
165             CABAL_EXEC=$(cygpath "$CABAL_EXEC")
166           fi
167           if [[ "${{ runner.os }}" == "macOS" ]]; then
168              # Workaround to avoid bsdtar corrupts the executable
169              # so executing it after untar throws `cannot execute binary file`
170              # see https://github.com/actions/virtual-environments/issues/2619#issuecomment-788397841
171              sudo /usr/sbin/purge
172           fi
173           DIR=$(dirname "$CABAL_EXEC")
174           FILE=$(basename "$CABAL_EXEC")
175           CABAL_EXEC_TAR="cabal-head-${{ runner.os }}-x86_64.tar.gz"
176           tar -czvf "$CABAL_EXEC_TAR" -C "$DIR" "$FILE"
177           echo "CABAL_EXEC_TAR=$CABAL_EXEC_TAR" >> "$GITHUB_ENV"
179       # We upload the cabal executable built with the ghc used in the release for:
180       # - Reuse it in the dogfooding job (although we could use the cached build dir)
181       # - Make it available in the workflow to make easier testing it locally
182       - name: Upload cabal-install executable to workflow artifacts
183         if: matrix.ghc == env.GHC_FOR_RELEASE
184         uses: actions/upload-artifact@v3
185         with:
186           name: cabal-${{ runner.os }}-x86_64
187           path: ${{ env.CABAL_EXEC_TAR }}
189       - name: Validate lib-tests
190         env:
191           # `rawSystemStdInOut reports text decoding errors`
192           # test does not find ghc without the full path in windows
193           GHCPATH: ${{ steps.setup-haskell.outputs.ghc-exe }}
194         run: sh validate.sh $FLAGS -s lib-tests
196       - name: Validate lib-suite
197         run: sh validate.sh $FLAGS -s lib-suite
199       - name: Validate cli-tests
200         run: sh validate.sh $FLAGS -s cli-tests
202       - name: Validate cli-suite
203         run: sh validate.sh $FLAGS -s cli-suite
205       - name: Validate solver-benchmarks-tests
206         if: matrix.ghc == env.GHC_FOR_SOLVER_BENCHMARKS
207         run: sh validate.sh $FLAGS -s solver-benchmarks-tests
209       - name: Validate solver-benchmarks-run
210         if: matrix.ghc == env.GHC_FOR_SOLVER_BENCHMARKS
211         run: sh validate.sh $FLAGS -s solver-benchmarks-run
214   validate-old-ghcs:
215     name: Validate old ghcs ${{ matrix.extra-ghc }}
216     runs-on: ubuntu-latest
217     needs: validate
219     strategy:
220       matrix:
221         extra-ghc: ['8.4.4', '8.2.2', '8.0.2']
222           ## GHC 7.10.3 does not install on ubuntu-22.04 with ghcup.
223           ## Older GHCs are not supported by ghcup in the first place.
224       fail-fast: false
226     steps:
228       - uses: actions/checkout@v4
230       - name: Install prerequisites for old GHCs
231         run: |
232           sudo apt-get update
233           sudo apt-get install libncurses5 libtinfo5
235       - name: Install extra compiler
236         run: ghcup install ghc ${{ matrix.extra-ghc }}
238       - name: GHCup logs
239         if: always()
240         run: cat /usr/local/.ghcup/logs/*
242       - name: Install primary compiler
243         uses: haskell-actions/setup@v2
244         id: setup-haskell
245         with:
246           ghc-version: ${{ env.GHC_FOR_RELEASE }}
247           cabal-version: latest
249       - name: GHC versions
250         run: |
251           ghc --version
252           "ghc-${{ matrix.extra-ghc }}" --version
254       # As we are reusing the cached build dir from the previous step
255       # the generated artifacts are available here,
256       # including the cabal executable and the test suite
257       - uses: actions/cache@v4
258         with:
259           path: |
260             ${{ steps.setup-haskell.outputs.cabal-store }}
261             dist-*
262           key: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-${{ github.sha }}
263           restore-keys: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-
265       - name: Validate build
266         run: sh validate.sh ${{ env.COMMON_FLAGS }} -s build
268       - name: "Validate lib-suite-extras --extra-hc ghc-${{ matrix.extra-ghc }}"
269         env:
270           EXTRA_GHC: ghc-${{ matrix.extra-ghc }}
271         run: sh validate.sh ${{ env.COMMON_FLAGS }} --lib-only -s lib-suite-extras --extra-hc "${{ env.EXTRA_GHC }}"
273   build-alpine:
274     name: Build statically linked using alpine
275     runs-on: ubuntu-latest
276     container: 'alpine:3.19'
277     steps:
278       - name: Install extra dependencies
279         shell: sh
280         run: |
281           apk add bash curl sudo jq pkgconfig \
282           zlib-dev zlib-static binutils-gold curl \
283           gcc g++ gmp-dev libc-dev libffi-dev make \
284           musl-dev ncurses-dev perl tar xz
286       - uses: actions/checkout@v4
288       # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
289       - name: Manually supplied constraints/allow-newer
290         if: github.event_name == 'workflow_dispatch'
291         run: |
292           echo "allow-newer: ${ALLOWNEWER}"  >> cabal.validate.project
293           echo "constraints: ${CONSTRAINTS}" >> cabal.validate.project
295       - uses: haskell-actions/setup@v2
296         id: setup-haskell
297         with:
298           ghc-version: ${{ env.GHC_FOR_RELEASE }}
299           cabal-version: latest # latest is mandatory for cabal-testsuite, see https://github.com/haskell/cabal/issues/8133
301       #  See the following link for a breakdown of the following step
302       #  https://github.com/haskell/actions/issues/7#issuecomment-745697160
303       - uses: actions/cache@v4
304         with:
305           # validate.sh uses a special build dir
306           path: |
307             ${{ steps.setup-haskell.outputs.cabal-store }}
308             dist-*
309           key: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-${{ github.sha }}
310           restore-keys: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-
312       - name: Enable statically linked executables
313         run: |
314           echo 'executable-static: true' >> cabal.validate.project
316       - name: Build
317         run: sh validate.sh $FLAGS -s build
319       - name: Tar cabal head executable
320         run: |
321           CABAL_EXEC=$(cabal list-bin --builddir=dist-newstyle-validate-ghc-${{ env.GHC_FOR_RELEASE }} --project-file=cabal.validate.project cabal-install:exe:cabal)
322           # We have to tar the executable to preserve executable permissions
323           # see https://github.com/actions/upload-artifact/issues/38
324           DIR=$(dirname "$CABAL_EXEC")
325           FILE=$(basename "$CABAL_EXEC")
326           CABAL_EXEC_TAR="cabal-head-${{ runner.os }}-static-x86_64.tar.gz"
327           tar -czvf "$CABAL_EXEC_TAR" -C "$DIR" "$FILE"
328           echo "CABAL_EXEC_TAR=$CABAL_EXEC_TAR" >> "$GITHUB_ENV"
330       - name: Upload cabal-install executable to workflow artifacts
331         uses: actions/upload-artifact@v3
332         with:
333           name: cabal-${{ runner.os }}-static-x86_64
334           path: ${{ env.CABAL_EXEC_TAR }}
337   # The previous jobs use a released version of cabal to build cabal HEAD itself
338   # This one uses the cabal HEAD generated executable in the previous step
339   # to build itself again, as sanity check
340   dogfooding:
341     name: Dogfooding ${{ matrix.os }} ghc-${{ matrix.ghc }}
342     runs-on: ${{ matrix.os }}
343     needs: validate
344     strategy:
345       matrix:
346         os: [ubuntu-latest, macos-13, windows-latest]
347         # We only use one ghc version the used one for the next release (defined at top of the workflow)
348         # We need to build an array dynamically to inject the appropiate env var in a previous job,
349         # see https://docs.github.com/en/actions/learn-github-actions/expressions#fromjson
350         ghc: ${{ fromJSON (needs.validate.outputs.GHC_FOR_RELEASE) }}
352     steps:
353       - name: Work around XDG directories existence (haskell-actions/setup#62)
354         if: runner.os == 'macOS'
355         run: |
356           rm -rf ~/.config/cabal
357           rm -rf ~/.cache/cabal
359       - uses: actions/checkout@v4
361       - uses: haskell-actions/setup@v2
362         id: setup-haskell
363         with:
364           ghc-version: ${{ matrix.ghc }}
365           cabal-version: latest # default, we are not using it in this job
367       - name: Download cabal executable from workflow artifacts
368         uses: actions/download-artifact@v3
369         with:
370           name: cabal-${{ runner.os }}-x86_64
371           path: cabal-head
373       - name: Untar the cabal executable
374         run: tar -xzf "./cabal-head/cabal-head-${{ runner.os }}-x86_64.tar.gz" -C cabal-head
376       - name: print-config using cabal HEAD
377         run: sh validate.sh ${{ env.COMMON_FLAGS }} --with-cabal ./cabal-head/cabal -s print-config
379       # We dont use cache to force a build with a fresh store dir and build dir
380       # This way we check cabal can build all its dependencies
381       - name: Build using cabal HEAD
382         run: sh validate.sh ${{ env.COMMON_FLAGS }} --with-cabal ./cabal-head/cabal -s build
384   prerelease-head:
385     name: Create a GitHub prerelease with the binary artifacts
386     runs-on: ubuntu-latest
387     if: github.ref == 'refs/heads/master'
389     # IMPORTANT! Any job added to the workflow should be added here too
390     needs: [validate, validate-old-ghcs, build-alpine, dogfooding]
392     steps:
393     - uses: actions/download-artifact@v3
394       with:
395         name: cabal-Windows-x86_64
397     - uses: actions/download-artifact@v3
398       with:
399         name: cabal-Linux-x86_64
401     - uses: actions/download-artifact@v3
402       with:
403         name: cabal-Linux-static-x86_64
405     - uses: actions/download-artifact@v3
406       with:
407         name: cabal-macOS-x86_64
409     - name: Create GitHub prerelease
410       uses: marvinpinto/action-automatic-releases@v1.2.1
411       with:
412         repo_token: ${{ secrets.GITHUB_TOKEN }}
413         automatic_release_tag: cabal-head
414         prerelease: true
415         title: cabal-head
416         files: |
417           cabal-head-Windows-x86_64.tar.gz
418           cabal-head-Linux-x86_64.tar.gz
419           cabal-head-Linux-static-x86_64.tar.gz
420           cabal-head-macOS-x86_64.tar.gz
422   # We use this job as a summary of the workflow
423   # It will fail if any of the previous jobs does it
424   # This way we can use it exclusively in branch protection rules
425   # and abstract away the concrete jobs of the workflow, including their names
426   validate-post-job:
427     if: always()
428     name: Validate post job
429     runs-on: ubuntu-latest
430     # IMPORTANT! Any job added to the workflow should be added here too
431     needs: [validate, validate-old-ghcs, build-alpine, dogfooding]
433     steps:
434       - run: |
435           echo "jobs info: ${{ toJSON(needs) }}"
436       - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
437         run: exit 1