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