-
Notifications
You must be signed in to change notification settings - Fork 697
540 lines (475 loc) · 19.9 KB
/
validate.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
name: Validate
# See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency.
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
# Note: This workflow file contains the required job "Validate post job". We are using path filtering
# here to ignore PRs which only change documentation. This can cause a problem, see the workflow file
# "validate.skip.yml" for a description of the problem and the solution provided in that file.
on:
push:
paths-ignore:
- "doc/**"
- "**/README.md"
- "CONTRIBUTING.md"
- "changelog.d/**"
# only top level for these, because various test packages have them too
- "*/ChangeLog.md"
- "*/changelog.md"
- "release-notes/**"
branches:
- master
# hardcoded LTS branch, change when new LTS released!
- '3.12'
pull_request:
paths-ignore:
- "doc/**"
- "**/README.md"
- "CONTRIBUTING.md"
- "changelog.d/**"
- "*/ChangeLog.md"
- "*/changelog.md"
- "release-notes/**"
release:
types:
- created
workflow_call:
# See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
workflow_dispatch:
inputs:
allow-newer:
description: allow-newer line
required: false
type: string
constraints:
description: constraints line
required: false
type: string
env:
# We choose a stable ghc version across all os's
# which will be used to do the next release
GHC_FOR_RELEASE: "9.4.8"
# Ideally we should use the version about to be released for hackage tests and benchmarks
GHC_FOR_SOLVER_BENCHMARKS: "9.4.8"
GHC_FOR_COMPLETE_HACKAGE_TESTS: "9.4.8"
COMMON_FLAGS: "-j 2 -v"
# See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
ALLOWNEWER: ${{ github.event.inputs.allow-newer }}
CONSTRAINTS: ${{ github.event.inputs.constraints }}
jobs:
validate:
name: Validate ${{ matrix.sys.os }} ghc-${{ matrix.ghc }}
runs-on: ${{ matrix.sys.os }}
outputs:
GHC_FOR_RELEASE: ${{ format('["{0}"]', env.GHC_FOR_RELEASE) }}
strategy:
fail-fast: false
matrix:
sys:
- { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
- { os: ubuntu-22.04, shell: bash }
- { os: macos-latest, shell: bash }
# If you remove something from here, then add it to the old-ghcs job.
# Also a removed GHC from here means that we are actually dropping
# support, so the PR *must* have a changelog entry.
ghc:
[
"9.10.1",
"9.8.2",
"9.6.6",
"9.4.8",
"9.2.8",
"9.0.2",
"8.10.7",
"8.8.4",
]
exclude:
# Throws fatal "cabal-tests.exe: fd:8: hGetLine: end of file" exception
# even with --io-manager=native
- sys:
{ os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
ghc: "9.0.2"
# corrupts GHA cache or the fabric of reality itself, see https://github.com/haskell/cabal/issues/8356
- sys:
{ os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
ghc: "8.10.7"
# lot of segfaults caused by ghc bugs
- sys:
{ os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
ghc: "8.8.4"
# ghc before 8.10.5 doesn't run on AArch64
# 9.0.2 suffers from https://gitlab.haskell.org/ghc/ghc/-/issues/20592
# 8.10.7 throws asm errors in hashable's cbits suggesting the runner doesn't
# support a CPU extension for hardware SHA; may be fixable with flags
- sys:
{ os: macos-latest, shell: bash }
ghc: "9.0.2"
- sys:
{ os: macos-latest, shell: bash }
ghc: "8.10.7"
- sys:
{ os: macos-latest, shell: bash }
ghc: "8.8.4"
defaults:
run:
shell: ${{ matrix.sys.shell }}
steps:
- name: Work around XDG directories existence (haskell-actions/setup#62)
if: runner.os == 'macOS'
run: |
rm -rf ~/.config/cabal
rm -rf ~/.cache/cabal
- name: "WIN: Setup TMP environment variable"
if: runner.os == 'Windows'
run: |
echo "TMP=${{ runner.temp }}" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
# See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
- name: Add manually supplied allow-newer
if: github.event_name == 'workflow_dispatch' && github.event.inputs.allow-newer != ''
run: |
echo "allow-newer: ${{ github.event.inputs.allow-newer }}" >> cabal.validate.project
- name: Add manually supplied constraints
if: github.event_name == 'workflow_dispatch' && github.event.inputs.constraints != ''
run: |
echo "constraints: ${{ github.event.inputs.constraints }}" >> cabal.validate.project
- uses: haskell-actions/setup@v2
id: setup-haskell
with:
ghc-version: ${{ matrix.ghc }}
cabal-version: 3.12.1.0 # see https://github.com/haskell/cabal/pull/10251
ghcup-release-channel: https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.8.yaml
# See the following link for a breakdown of the following step
# https://github.com/haskell/actions/issues/7#issuecomment-745697160
- uses: actions/cache@v4
with:
# validate.sh uses a special build dir
path: |
${{ steps.setup-haskell.outputs.cabal-store }}
dist-*
key: ${{ runner.os }}-${{ matrix.ghc }}-${{ github.sha }}
restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-
# 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: cabal install --ignore-project hackage-repo-tool
# Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs
- name: "MAC: Install Autotools"
if: runner.os == 'macOS'
run: brew install automake
# Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs
- name: "WIN: Install Autotools"
if: runner.os == 'Windows'
run: /usr/bin/pacman --noconfirm -S autotools
- name: Set validate inputs
run: |
FLAGS="${{ env.COMMON_FLAGS }}"
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: Canonicalize architecture
run: |
case ${{ runner.arch }} in
X86) arch=i386 ;;
X64) arch=x86_64 ;;
ARM64) arch=aarch64 ;;
*) echo "Unsupported architecture, please fix validate.yaml" 2>/dev/null; exit 1 ;;
esac
echo "CABAL_ARCH=$arch" >> "$GITHUB_ENV"
- name: Tar cabal head executable
if: matrix.ghc == env.GHC_FOR_RELEASE
run: |
CABAL_EXEC=$(cabal list-bin --builddir=dist-newstyle-validate-ghc-${{ matrix.ghc }} --project-file=cabal.validate.project 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 list-bin` 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
DIR=$(dirname "$CABAL_EXEC")
FILE=$(basename "$CABAL_EXEC")
CABAL_EXEC_TAR="cabal-head-${{ runner.os }}-$CABAL_ARCH.tar.gz"
tar -czvf "$CABAL_EXEC_TAR" -C "$DIR" "$FILE"
echo "CABAL_EXEC_TAR=$CABAL_EXEC_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.ghc == env.GHC_FOR_RELEASE
uses: actions/upload-artifact@v4
with:
name: cabal-${{ runner.os }}-${{ env.CABAL_ARCH }}
path: ${{ env.CABAL_EXEC_TAR }}
- name: Validate 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: |
set +e
rc=0
tests="lib-tests lib-suite cli-tests cli-suite"
if [ "${{ matrix.ghc }}" = "${{ env.GHC_FOR_SOLVER_BENCHMARKS }}" ]; then
tests="$tests solver-benchmarks-tests solver-benchmarks-run"
fi
for test in $tests; do
echo Validate "$test"
sh validate.sh $FLAGS -s "$test" || rc=1
echo End "$test"
done
exit $rc
# The above ensures all the tests get run, for a single platform+ghc.
# Trying to ensure they run for *all* combinations but still fail
# at the end seems to be extremely difficult at best. It's doable,
# but it requires a continuously growing stack of conditions and
# one possibly nightmarish final conditional. 'fail-fast' gets us
# partway there, at least, but is still imperfect.
validate-old-ghcs:
name: Validate old ghcs ${{ matrix.extra-ghc }}
runs-on: ubuntu-22.04
needs: validate
strategy:
matrix:
extra-ghc:
["8.4.4", "8.2.2", "8.0.2"]
## GHC 7.10.3 does not install on ubuntu-22.04 with ghcup.
## Older GHCs are not supported by ghcup in the first place.
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Install prerequisites for old GHCs
run: |
sudo apt-get update
sudo apt-get install libncurses5 libtinfo5
- name: Install extra compiler
run: ghcup install ghc ${{ matrix.extra-ghc }}
- name: GHCup logs
if: always()
run: cat /usr/local/.ghcup/logs/*
- name: Install primary compiler
uses: haskell-actions/setup@v2
id: setup-haskell
with:
ghc-version: ${{ env.GHC_FOR_RELEASE }}
cabal-version: latest
- name: GHC versions
run: |
ghc --version
"ghc-${{ matrix.extra-ghc }}" --version
# 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@v4
with:
path: |
${{ steps.setup-haskell.outputs.cabal-store }}
dist-*
key: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-${{ github.sha }}
restore-keys: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-
- name: Validate build
id: build
run: sh validate.sh ${{ env.COMMON_FLAGS }} -s build
- name: "Validate lib-suite-extras --extra-hc ghc-${{ matrix.extra-ghc }}"
env:
EXTRA_GHC: ghc-${{ matrix.extra-ghc }}
run: sh validate.sh ${{ env.COMMON_FLAGS }} --lib-only -s lib-suite-extras --extra-hc "${{ env.EXTRA_GHC }}"
# See the comment above about running all tests but still failing if one
# of them does; it also applies here.
build-alpine:
name: Build statically linked using alpine
runs-on: ubuntu-latest
container: "alpine:3.19"
steps:
- name: Install extra dependencies
shell: sh
run: |
apk add bash curl sudo jq pkgconfig \
zlib-dev zlib-static binutils-gold curl \
gcc g++ gmp-dev libc-dev libffi-dev make \
musl-dev ncurses-dev perl tar xz
- uses: actions/checkout@v4
# See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
- name: Manually supplied constraints/allow-newer
if: github.event_name == 'workflow_dispatch'
run: |
echo "allow-newer: ${ALLOWNEWER}" >> cabal.validate.project
echo "constraints: ${CONSTRAINTS}" >> cabal.validate.project
- uses: haskell-actions/setup@v2
id: setup-haskell
with:
ghc-version: ${{ env.GHC_FOR_RELEASE }}
cabal-version: latest # latest is mandatory for cabal-testsuite, see https://github.com/haskell/cabal/issues/8133
# See the following link for a breakdown of the following step
# https://github.com/haskell/actions/issues/7#issuecomment-745697160
- uses: actions/cache@v4
with:
# validate.sh uses a special build dir
path: |
${{ steps.setup-haskell.outputs.cabal-store }}
dist-*
key: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-${{ github.sha }}
restore-keys: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-
- name: Enable statically linked executables
run: |
echo 'executable-static: true' >> cabal.validate.project
- name: Build
run: sh validate.sh $FLAGS -s build
- name: Tar cabal head executable
run: |
CABAL_EXEC=$(cabal list-bin --builddir=dist-newstyle-validate-ghc-${{ env.GHC_FOR_RELEASE }} --project-file=cabal.validate.project cabal-install:exe:cabal)
# We have to tar the executable to preserve executable permissions
# see https://github.com/actions/upload-artifact/issues/38
DIR=$(dirname "$CABAL_EXEC")
FILE=$(basename "$CABAL_EXEC")
CABAL_EXEC_TAR="cabal-head-${{ runner.os }}-static-x86_64.tar.gz"
tar -czvf "$CABAL_EXEC_TAR" -C "$DIR" "$FILE"
echo "CABAL_EXEC_TAR=$CABAL_EXEC_TAR" >> "$GITHUB_ENV"
- name: Upload cabal-install executable to workflow artifacts
uses: actions/upload-artifact@v4
with:
name: cabal-${{ runner.os }}-static-x86_64
path: ${{ env.CABAL_EXEC_TAR }}
# 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.sys.os }} ghc-${{ matrix.ghc }}
runs-on: ${{ matrix.sys.os }}
needs: validate
strategy:
matrix:
sys:
- { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
- { os: ubuntu-22.04, shell: bash }
- { os: macos-latest, shell: bash }
# 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) }}
defaults:
run:
shell: ${{ matrix.sys.shell }}
steps:
# TODO: make a reusable action for this
- name: Canonicalize architecture
run: |
case ${{ runner.arch }} in
X86) arch=i386 ;;
X64) arch=x86_64 ;;
ARM64) arch=aarch64 ;;
*) echo "Unsupported architecture" 2>/dev/null; exit 1 ;;
esac
echo "CABAL_ARCH=$arch" >> "$GITHUB_ENV"
- name: "MAC: Work around XDG directories existence (haskell-actions/setup#62)"
if: runner.os == 'macOS'
run: |
rm -rf ~/.config/cabal
rm -rf ~/.cache/cabal
- name: "WIN: Setup TMP environment variable"
if: runner.os == 'Windows'
run: |
echo "TMP=${{ runner.temp }}" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
- 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: Download cabal executable from workflow artifacts
uses: actions/download-artifact@v4
with:
name: cabal-${{ runner.os }}-${{ env.CABAL_ARCH }}
path: cabal-head
- name: Untar the cabal executable
run: tar -xzf "./cabal-head/cabal-head-${{ runner.os }}-$CABAL_ARCH.tar.gz" -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
prerelease-head:
name: Create a GitHub prerelease with the binary artifacts
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
permissions:
contents: write
# IMPORTANT! Any job added to the workflow should be added here too
needs: [validate, validate-old-ghcs, build-alpine, dogfooding]
steps:
# for now this is hardcoded. is there a better way?
- uses: actions/download-artifact@v4
with:
pattern: cabal-*
path: binaries
merge-multiple: true
- name: Create GitHub prerelease
uses: softprops/action-gh-release@v2
with:
tag_name: cabal-head
prerelease: true
files: binaries/cabal-*
prerelease-lts:
name: Create a GitHub LTS prerelease with the binary artifacts
runs-on: ubuntu-latest
# The LTS branch is hardcoded for now, update it on a new LTS!
# if: github.ref == 'refs/heads/3.12'
permissions:
contents: write
# IMPORTANT! Any job added to the workflow should be added here too
needs: [validate, validate-old-ghcs, build-alpine, dogfooding]
steps:
- uses: actions/download-artifact@v4
with:
pattern: cabal-*
path: binaries
merge-multiple: true
- run: |
# bash-ism, but we forced bash above
cd binaries
for f in cabal-*; do
mv "$f" "cabal-lts-${f##cabal-}"
done
- run: echo ${{ github.ref }}
- name: Create GitHub prerelease
if: github.ref == 'refs/heads/3.12'
uses: softprops/action-gh-release@v2
with:
tag_name: cabal-lts-head
prerelease: true
files: binaries/cabal-*
# We use this job as a summary of the workflow
# It will fail if any of the previous jobs does
# 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-latest
# IMPORTANT! Any job added to the workflow should be added here too
needs: [validate, validate-old-ghcs, build-alpine, dogfooding]
steps:
- run: |
echo "jobs info: ${{ toJSON(needs) }}"
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1