diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 00000000000..dd925bece99 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,68 @@ +Actions +--- + +We use a number of local composite actions in `validate.yml`. Two of them simplify +`validate.yml` considerably; the other two less so, but will be useful in a future +tier-2 job to ensure that the main and tier 2 validate operations stay in sync. + +We also use a central configuration file `.github/config.yml` containing the +various magic numbers/lists that bootstrap and validate use. The `Makefile` +also gets some information from it so it can stay in sync with `bootstrap.yml`. + +The actions, found in `.github/actions` per convention, are: + +- `cabal-setup`: does all the preliminary setup for a Cabal job +- `validate-build`: does `cabal-setup` and then builds cabal for validate +- `validate-old`: does `cabal-setup` and then validates a `validate-build` cabal against older GHCs +- `dogfooding`: does `cabal-setup` and then uses a `validate-build` cabal to build itself + +As yet, there are no actions for the tests, because they're just a couple lines +of shell aside from `cabal-setup`. This may change in the future. + +--- + +Workflows +--- + +The standard workflows are: + +- `bootstrap.yml`: bootstrap a cabal from prepared JSONs (see `make bootstrap-jsons`) +- `validate.yml`: build a cabal with extra assertions and run the full test suite on it +- `changelogs.yml`: validate `changelog.d` files using [`changelog-d`] +- `dependabot.yml`: check `dependabot` configuration (sadly, not automatic; we lifted this from Ubuntu's CI) +- `lint.yml`: run `hlint` on cabal sources +- `format.yml`: check source formatting using Fourmolu v0.12 +- `quick-jobs.yml`: various (formerly) quick checks +- `typos.yml`: look for typos in documentation +- `users-guide.yml`: generate the users guide, creating an artifact +- `whitespace.yml`: check for extraneous whitespace in various files +- `check-sdist.yml`: make sure cabal can be built against the `Cabal` bootlib (see e.g. #10931, #9863) + +The validate workflow performs a number of tests on tier-1 platforms: + +- on current GHCs (see `GHC_FOR_VALIDATE` and `GHC_FOR_VALIDATE_ONLY` in `config.yml`) it runs through the full suite of tests (`lib-tests`, `lib-suite`, `cli-tests`, and `cli-suite`) +- on older GHCs (see `GHC_FOR_VALIDATE_OLD`) it only runs `lib-suite-extras`, which is a cut-down test suite ("extras" refers to the fact that `validate.yml` historically referred to the old GHCs as `extra-ghc`) +- it builds but doesn't validate (for some reason) a static `cabal` on Alpine with MUSL +- it dogfoods `cabal` by having it build itself + +The bootstrap workflow verifies that cabal can be built from pregenerated JSONs, for use in bootstrapping cabal on a new platform (since cabal is self-hosted). + +--- + +Support tiers +--- + +Currently we support the following platforms as Tier 1: + +- MacOS on AArch64 +- X86-64 (aka AMD64) +- Windows (10 and 11) + +Tier 2 platforms are: + +- FreeBSD (AMD64 only) +- Alpine/MUSL static build +- MacOS on Intel +- X86 (deprecated) + +We do not currently test on tier 2 platforms, but support for that is coming. diff --git a/.github/actions/cabal-setup/action.yml b/.github/actions/cabal-setup/action.yml new file mode 100644 index 00000000000..e6652e4125a --- /dev/null +++ b/.github/actions/cabal-setup/action.yml @@ -0,0 +1,164 @@ +# +# Set up a workflow for building Cabal +# +# The only required input is the ghc version to build with (`ghc`). The other inputs are +# generally for special purposes: +# +# - an additional ghc version for testing, used by the old-versions tests (`extra-ghc`) +# - the `cabal.project` variant to use (`validate`) +# - `allow-newer` and `constraints` lines, for manual jobs +# - whether to build a static executable (for Alpine) (`static`) +# - shell (override on Windows because the default is the wrong version) (`shell`) +# - whether to use the cache (`with_cache`) +# +# There is only one output: the path to the installed (main) ghc. +# +# This is automatically done by the `validate-build` action, which takes the same +# parameters. It should be done directly for jobs that test the built `cabal`. +# + +name: Cabal setup +description: Set up a workflow for Cabal + +inputs: + ghc: + description: ghc version to use + required: true + extra-ghc: + description: additional ghc for tests + required: false + default: '' + project: + description: which cabal.project to use + required: false + default: 'validate' + allow-newer: + description: allow-newer line + required: false + default: '' + constraints: + description: constraints line + required: false + default: '' + static: + description: whether to build statically + required: false + default: 'false' + shell: + description: shell to use + required: false + default: 'bash' + with_cache: + description: whether to instantiate cache + required: false + default: 'true' + +outputs: + ghc-exe: + description: Path to ghc installed by setup-haskell + value: ${{ steps.setup-haskell.outputs.ghc-exe }} + +runs: + using: composite + steps: + - name: Make sure ghc is specified + if: inputs.ghc == '' + shell: ${{ inputs.shell }} + run: exit 1 + + - name: Work around existence of XDG directories (haskell-actions/setup#62) + if: runner.os == 'macOS' + shell: ${{ inputs.shell }} + run: | + rm -rf ~/.config/cabal + rm -rf ~/.cache/cabal + + - name: "WIN: Setup TMP environment variable" + if: runner.os == 'Windows' + shell: ${{ inputs.shell }} + run: | + echo "TMP=${{ runner.temp }}" >> "$GITHUB_ENV" + + # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions + - name: Add manually supplied allow-newer + if: inputs.allow-newer != '' + shell: ${{ inputs.shell }} + run: | + echo "allow-newer: ${{ inputs.allow-newer }}" >> cabal.${{ inputs.project }}.project + + - name: Add manually supplied constraints + if: inputs.constraints != '' + shell: ${{ inputs.shell }} + run: | + echo "constraints: ${{ inputs.constraints }}" >> cabal.${{ inputs.project }}.project + + - name: Enable statically linked executables + if: inputs.static == 'true' + shell: ${{ inputs.shell }} + run: | + echo 'executable-static: true' >> cabal.${{ inputs.project }}.project + + # must happen before the main setup so the correct ghc is default + - name: Install extra ghc for tests + if: inputs.extra-ghc != '' + uses: haskell-actions/setup@v2 + with: + ghc-version: ${{ inputs.extra-ghc }} + cabal-version: '3.12.1.0' # see https://github.com/haskell/cabal/pull/10251 + + - uses: haskell-actions/setup@v2 + id: setup-haskell + with: + ghc-version: ${{ inputs.ghc }} + cabal-version: '3.12.1.0' + # do we use this? + ghcup-release-channel: https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.9.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 + if: inputs.with_cache != 'false' + with: + # cabal-validate uses a special build dir + path: | + ${{ steps.setup-haskell.outputs.cabal-store }} + dist-* + key: ${{ runner.os }}-${{ inputs.ghc }}-${{ github.sha }} + restore-keys: ${{ runner.os }}-${{ inputs.ghc }}- + + # Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs + - name: "MAC: Install Autotools" + if: runner.os == 'macOS' + shell: ${{ inputs.shell }} + run: | + brew install automake + + # Needed by cabal-testsuite/PackageTests/Configure/setup.test.hs + - name: "WIN: Install Autotools" + if: runner.os == 'Windows' + shell: ${{ inputs.shell }} + run: | + /usr/bin/pacman --noconfirm -S autotools + + - name: Set validate inputs + shell: ${{ inputs.shell }} + run: | + FLAGS="$COMMON_FLAGS" + if [[ "${{ inputs.ghc }}" == "$GHC_FOR_SOLVER_BENCHMARKS" ]]; then + FLAGS="$FLAGS --solver-benchmarks" + fi + if [[ "${{ inputs.ghc }}" == "$GHC_FOR_COMPLETE_HACKAGE_TESTS" ]]; then + FLAGS="$FLAGS --complete-hackage-tests" + fi + echo "FLAGS=$FLAGS" >> "$GITHUB_ENV" + + - name: Canonicalize architecture + shell: ${{ inputs.shell }} + run: | + case ${{ runner.arch }} in + X86) arch=i386 ;; + X64) arch=x86_64 ;; + ARM64) arch=aarch64 ;; + *) echo "Unsupported architecture, please fix cabal-setup/action.yml" 2>/dev/null; exit 1 ;; + esac + echo "CABAL_ARCH=$arch" >> "$GITHUB_ENV" diff --git a/.github/actions/dogfooding/action.yml b/.github/actions/dogfooding/action.yml new file mode 100644 index 00000000000..782bee55bc0 --- /dev/null +++ b/.github/actions/dogfooding/action.yml @@ -0,0 +1,61 @@ +# +# Verify that a `cabal` can build itself. Use the `validate-build` action to +# build the `cabal` being tested. This is separated out so it can be reused by +# a future tier-2 job. +# +# The only required input is the ghc version to build with (`ghc`). The other inputs are +# generally for special purposes: +# +# - `allow-newer` and `constraints` lines, for manual jobs +# - shell (override on Windows because the default is the wrong version) (`shell`) +# - whether to use the cache (`with_cache`) +# +# There is only one output: the path to the installed (main) ghc. +# + +name: Dogfooding cabal-install on a ghc/platform +description: Run a cabal-install uncached validate from a previously built binary + +inputs: + ghc: + description: ghc version to use + required: true + shell: + description: shell to use + required: false + default: 'bash' + allow-newer: + description: allow-newer line + required: false + constraints: + description: constraints line + required: false + +runs: + using: composite + steps: + - uses: ./.github/actions/cabal-setup + with: + ghc: ${{ inputs.ghc }} + shell: ${{ inputs.shell }} + allow-newer: ${{ inputs.allow_newer }} + constraints: ${{ inputs.constraints }} + # We don't use cache to force a build with a fresh store dir and build dir + # This way we check cabal can build all its dependencies + with_cache: 'false' + + - 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 + shell: ${{ inputs.shell }} + run: | + tar -xzf "./cabal-head/cabal-head-${{ runner.os }}-$CABAL_ARCH.tar.gz" -C cabal-head + + - name: Build using cabal HEAD + shell: ${{ inputs.shell }} + run: | + sh validate.sh $COMMON_FLAGS --with-cabal ./cabal-head/cabal -s build diff --git a/.github/actions/validate-build/action.yml b/.github/actions/validate-build/action.yml new file mode 100644 index 00000000000..6c790beff59 --- /dev/null +++ b/.github/actions/validate-build/action.yml @@ -0,0 +1,102 @@ +# +# Build `cabal` for validation tests +# +# The only required input is the ghc version to build with (`ghc`). The other inputs are +# generally for special purposes: +# +# - `allow-newer` and `constraints` lines, for manual jobs +# - whether to build a static executable (for Alpine) (`static`) +# - shell (override on Windows because the default is the wrong version) (`shell`) +# - whether to use the cache (`with_cache`) +# + +name: Validate build +description: Build for a full validate on a ghc version + +inputs: + ghc: + description: ghc version to use + required: true + allow-newer: + description: allow-newer line + required: false + constraints: + description: constraints line + required: false + static: + description: whether to build statically + required: false + default: 'false' + shell: + description: shell to use + required: false + default: 'bash' + with_cache: + description: whether to instantiate cache + required: false + default: 'true' + +runs: + using: composite + steps: + - uses: ./.github/actions/cabal-setup + id: cabal-setup + with: + shell: ${{ inputs.shell }} + ghc: ${{ inputs.ghc }} + allow-newer: ${{ inputs.allow-newer }} + constraints: ${{ inputs.constraints }} + static: ${{ inputs.static }} + with_cache: ${{ inputs.with_cache }} + + # 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 + shell: ${{ inputs.shell }} + run: | + cabal install --ignore-project hackage-repo-tool + + - name: Validate build + shell: ${{ inputs.shell }} + run: | + echo ::group::Build + sh validate.sh $FLAGS -s build + + - name: Tar cabal head executable + if: inputs.ghc == env.GHC_FOR_RELEASE + shell: ${{ inputs.shell }} + run: | + echo ::group::Tar + CABAL_EXEC=$(cabal list-bin --builddir=dist-newstyle-validate-ghc-${{ inputs.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 corrupting the executable + # such that 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 }}${{ inputs.static == 'true' && '-static' || '' }}-$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 so we can: + # - 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 + # (Using the cache in dogfooding would defeat its purpose, though.) + - name: Upload cabal-install executable to workflow artifacts + if: inputs.ghc == env.GHC_FOR_RELEASE + uses: actions/upload-artifact@v4 + with: + name: cabal-${{ runner.os }}${{ inputs.static == 'true' && '-static' || '' }}-${{ env.CABAL_ARCH }} + path: ${{ env.CABAL_EXEC_TAR }} diff --git a/.github/actions/validate-old/action.yml b/.github/actions/validate-old/action.yml new file mode 100644 index 00000000000..4f7f3a1a0a9 --- /dev/null +++ b/.github/actions/validate-old/action.yml @@ -0,0 +1,55 @@ +# +# Validate that `cabal` can drive older ghc versions' `Cabal` libraries. Assumes it +# is being run in an Ubuntu container. This is separated out so it can be reused by +# a future tier-2 job. +# +# Inputs: +# - the ghc to use to build `cabal` (`ghc`) +# - the old ghc version to test (`extra-ghc`) +# - shell (override on Windows because the default is the wrong version) (`shell`) (optional) +# + +name: Validate old ghcs +description: Run a Cabal-only validate on an older ghc version + +inputs: + ghc: + description: ghc version to use + required: true + extra-ghc: + description: old ghc version to test + required: true + shell: + description: shell to use + required: false + default: 'bash' + +runs: + using: composite + steps: + - name: Install prerequisites for old GHCs + shell: ${{ inputs.shell }} + run: | + sudo apt-get update + sudo apt-get install libncurses5 libtinfo5 + + - uses: ./.github/actions/cabal-setup + with: + ghc: ${{ inputs.ghc }} + extra-ghc: ${{ inputs.extra-ghc }} + + - name: GHC versions + shell: ${{ inputs.shell }} + run: | + ghc --version + "ghc-${{ inputs.extra-ghc }}" --version + + - name: Validate build + shell: ${{ inputs.shell }} + run: | + sh validate.sh $COMMON_FLAGS -s build + + - name: "Validate lib-suite-extras --extra-hc ghc-${{ inputs.extra-ghc }}" + shell: ${{ inputs.shell }} + run: | + sh validate.sh $COMMON_FLAGS --lib-only -s lib-suite-extras --extra-hc "ghc-${{ inputs.extra-ghc }}" diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 00000000000..5a58e78065a --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,30 @@ +# +# Configuration for cabal validation and maybe bootstrapping later. +# Hopefully this will become the central configuration repository +# for cabal, with workflows, the `Makefile`, etc. pulling from it. +# +# This is a simplified YAML with a few extensions of its own, such +# as being able to reference the values of previously defined keys. +# + +# note: these three must be elements of GHC_FOR_VALIDATE! +GHC_FOR_RELEASE: "9.10.2" +GHC_FOR_SOLVER_BENCHMARKS: $(GHC_FOR_RELEASE) +GHC_FOR_COMPLETE_HACKAGE_TESTS: $(GHC_FOR_RELEASE) +# these will be decoded with fromJSON, and must be quoted to keep YAML from making objects +# If you remove something from here, then add it to GHC_FOR_VALIDATE_OLD. +# Also a removed GHC from here means that we are actually dropping +# support, so the PR *must* have a changelog entry. +# validate + bootstrap +GHC_FOR_VALIDATE: '["9.12.2", "9.10.2", "9.8.4", "9.6.7", "9.4.8", "9.2.8"]' +# validate only +GHC_FOR_VALIDATE_ONLY: '["9.0.2", "8.10.7", "8.8.4"]' +# only check that we can drive their Cabal, not a full validate run +## GHC 7.10.3 does not install on ubuntu-22.04 with ghcup. +## Older GHCs are not supported by ghcup in the first place. +GHC_FOR_VALIDATE_OLD: '["8.6.5", "8.4.4", "8.2.2", "8.0.2"]' +# @@@ Where's 8.6.5? +# +COMMON_FLAGS: "-j 2 -v" +# not yet used; LTSes now delayed until GHC also comes up with an LTS +LTS_BRANCH: "" diff --git a/.github/workflows/bootstrap.yml b/.github/workflows/bootstrap.yml index 6c92eb5ebed..c84b5c8d9e6 100644 --- a/.github/workflows/bootstrap.yml +++ b/.github/workflows/bootstrap.yml @@ -15,11 +15,64 @@ on: - created jobs: + + config: + runs-on: ubuntu-latest + # `matrix` can't access `env`, but it can access `outputs` from `needs` jobs. + # So we have to "export" some things here as job outputs. Additionally, we can't + # load `env` persistently from here. But we can export a string that can be `run` + # to load up the environment for individual jobs. This may change later: GitHub + # claims that YAML anchors are coming later in 2025. + outputs: + # expose the configuration variables for use by jobs + GHC_FOR_RELEASE: ${{ steps.conf.outputs['GHC_FOR_RELEASE'] }} + GHC_FOR_SOLVER_BENCHMARKS: ${{ steps.conf.outputs['GHC_FOR_SOLVER_BENCHMARKS'] }} + GHC_FOR_COMPLETE_HACKAGE_TESTS: ${{ steps.conf.outputs['GHC_FOR_COMPLETE_HACKAGE_TESTS'] }} + GHC_FOR_VALIDATE: ${{ steps.conf.outputs['GHC_FOR_VALIDATE'] }} + GHC_FOR_VALIDATE_ONLY: ${{ steps.conf.outputs['GHC_FOR_VALIDATE_ONLY'] }} + GHC_FOR_VALIDATE_OLD: ${{ steps.conf.outputs['GHC_FOR_VALIDATE_OLD'] }} + GHC_FOR_VALIDATE_ALL: ${{ steps.validates.outputs.result }} + COMMON_FLAGS: ${{ steps.conf.outputs['COMMON_FLAGS'] }} + LTS_RELEASE: ${{ steps.conf.outputs['LTS_RELEASE'] }} + # convenience for loading these into the environment + env: | + echo "GHC_FOR_RELEASE='${{ steps.conf.outputs.GHC_FOR_RELEASE }}'" >> "$GITHUB_ENV" + echo "GHC_FOR_SOLVER_BENCHMARKS='${{ steps.conf.outputs.GHC_FOR_SOLVER_BENCHMARKS }}'" >> "$GITHUB_ENV" + echo "GHC_FOR_COMPLETE_HACKAGE_TESTS='${{ steps.conf.outputs.GHC_FOR_COMPLETE_HACKAGE_TESTS }}'" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE='${{ steps.conf.outputs.GHC_FOR_VALIDATE }}'" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE_ONLY='${{ steps.conf.outputs.GHC_FOR_VALIDATE_ONLY }}'" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE_ALL='${{ steps.validates.outputs.result }}'" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE_OLD='${{ steps.conf.outputs.GHC_FOR_VALIDATE_OLD }}'" >> "$GITHUB_ENV" + echo "COMMON_FLAGS='${{ steps.conf.outputs.COMMON_FLAGS }}'" >> "$GITHUB_ENV" + echo "LTS_RELEASE='${{ steps.conf.outputs.LTS_RELEASE }}'" >> "$GITHUB_ENV" + steps: + - uses: actions/checkout@v4 + + - uses: pietrobolcato/action-read-yaml@1.1.0 + id: conf + with: + config: ${{ github.workspace }}/.github/config.yml + + # a bit of a hack: combine FOR_VALIDATE and FOR_VALIDATE_ONLY + - uses: actions/github-script@v7 + id: validates + env: + LIST1: ${{ steps.conf.outputs['GHC_FOR_VALIDATE'] }} + LIST2: ${{ steps.conf.outputs['GHC_FOR_VALIDATE_ONLY'] }} + with: + script: | + const list1 = process.env.LIST1 + const list2 = process.env.LIST2 + return [...JSON.parse(list1), ...JSON.parse(list2)] + bootstrap: + needs: config strategy: matrix: os: [ubuntu-latest] - ghc: ["9.2.8", "9.4.8", "9.6.7", "9.8.4", "9.10.2", "9.12.2"] + # see .github/config.yml + ghc: ${{ fromJSON(needs.config.outputs.GHC_FOR_VALIDATE) }} +# ghc: ${{ fromJSON('["9.12.2", "9.10.2", "9.8.4", "9.6.7", "9.4.8", "9.2.8"]') }} include: - os: macos-latest ghc: "9.2.8" @@ -41,6 +94,7 @@ jobs: restore-keys: bootstrap-${{ runner.os }}-${{ matrix.ghc }}-20221115- - uses: actions/checkout@v4 + - uses: haskell-actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index abf19bffab8..17ec645ac44 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -1,3 +1,18 @@ +# +# Main validate workflow +# +# This is substantially rewritten to reduce duplication by using composite actions +# (found in `.github/actions`). There is still some duplication that will require +# GitHub Actions to implement YAML anchors to remove. +# +# In addition, some things have been moved to actions so they can be reused by a +# future "tier 2" overnight job (assuming a way can be found to get GitHub to report +# them sanely). +# +# The old `prerelease` job has been removed, because it won't fire if a PR is pushed +# in without being built. Doing this correctly will require another overnight job. +# + name: Validate # See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency. @@ -9,8 +24,6 @@ on: push: branches: - master - # hardcoded LTS branch, change when new LTS released! - - '3.12' pull_request: release: types: @@ -29,47 +42,69 @@ on: 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.10.2" - # Ideally we should use the version about to be released for hackage tests and benchmarks - GHC_FOR_SOLVER_BENCHMARKS: "9.10.2" - GHC_FOR_COMPLETE_HACKAGE_TESTS: "9.10.2" - COMMON_FLAGS: "-j 2 -v" +jobs: - # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions - ALLOWNEWER: ${{ github.event.inputs.allow-newer }} - CONSTRAINTS: ${{ github.event.inputs.constraints }} + config: + runs-on: ubuntu-latest + # `matrix` can't access `env`, but it can access `outputs` from `needs` jobs. + # So we have to "export" some things here as job outputs. Additionally, we can't + # load `env` persistently from here. But we can export a string that can be `run` + # to load up the environment for individual jobs. This may change later: GitHub + # claims that YAML anchors are coming later in 2025. + outputs: + # expose the configuration variables for use by jobs + GHC_FOR_RELEASE: ${{ steps.conf.outputs['GHC_FOR_RELEASE'] }} + GHC_FOR_SOLVER_BENCHMARKS: ${{ steps.conf.outputs['GHC_FOR_SOLVER_BENCHMARKS'] }} + GHC_FOR_COMPLETE_HACKAGE_TESTS: ${{ steps.conf.outputs['GHC_FOR_COMPLETE_HACKAGE_TESTS'] }} + GHC_FOR_VALIDATE: ${{ steps.conf.outputs['GHC_FOR_VALIDATE'] }} + GHC_FOR_VALIDATE_ONLY: ${{ steps.conf.outputs['GHC_FOR_VALIDATE_ONLY'] }} + GHC_FOR_VALIDATE_OLD: ${{ steps.conf.outputs['GHC_FOR_VALIDATE_OLD'] }} + GHC_FOR_VALIDATE_ALL: ${{ steps.validates.outputs['result'] }} + COMMON_FLAGS: ${{ steps.conf.outputs['COMMON_FLAGS'] }} + LTS_RELEASE: ${{ steps.conf.outputs['LTS_RELEASE'] }} + # convenience for loading these into the environment + env: | + echo "GHC_FOR_RELEASE=${{ steps.conf.outputs['GHC_FOR_RELEASE'] }}" >> "$GITHUB_ENV" + echo "GHC_FOR_SOLVER_BENCHMARKS=${{ steps.conf.outputs['GHC_FOR_SOLVER_BENCHMARKS'] }}" >> "$GITHUB_ENV" + echo "GHC_FOR_COMPLETE_HACKAGE_TESTS=${{ steps.conf.outputs['GHC_FOR_COMPLETE_HACKAGE_TESTS'] }}" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE=${{ steps.conf.outputs['GHC_FOR_VALIDATE'] }}" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE_ONLY=${{ steps.conf.outputs['GHC_FOR_VALIDATE_ONLY'] }}" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE_ALL=${{ steps.validates.outputs['result'] }}" >> "$GITHUB_ENV" + echo "GHC_FOR_VALIDATE_OLD=${{ steps.conf.outputs['GHC_FOR_VALIDATE_OLD'] }}" >> "$GITHUB_ENV" + echo "COMMON_FLAGS=${{ steps.conf.outputs['COMMON_FLAGS'] }}" >> "$GITHUB_ENV" + echo "LTS_RELEASE=${{ steps.conf.outputs['LTS_RELEASE'] }}" >> "$GITHUB_ENV" + steps: + - uses: actions/checkout@v4 -jobs: - validate: - name: Validate ${{ matrix.sys.os }} ghc-${{ matrix.ghc }} + - uses: pietrobolcato/action-read-yaml@1.1.0 + id: conf + with: + config: ${{ github.workspace }}/.github/config.yml + + # a bit of a hack: combine FOR_VALIDATE and FOR_VALIDATE_ONLY + - uses: actions/github-script@v7 + env: + LIST1: ${{ steps.conf.outputs['GHC_FOR_VALIDATE'] }} + LIST2: ${{ steps.conf.outputs['GHC_FOR_VALIDATE_ONLY'] }} + id: validates + with: + script: return JSON.parse(process.env.LIST1).concat(JSON.parse(process.env.LIST2)) + + validate-build: + name: Validate build ${{ matrix.sys.os }} ghc-${{ matrix.ghc }} runs-on: ${{ matrix.sys.os }} - outputs: - GHC_FOR_RELEASE: ${{ format('["{0}"]', env.GHC_FOR_RELEASE) }} + needs: config + # This gets repeated later. YAML anchors, if they eventuate, will improve this. strategy: fail-fast: false matrix: sys: + # 'bash' on Windows apparently gets you the one from Git for Windows, + # whereas this needs the one from msys - { 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.12.2", - "9.10.2", - "9.8.4", - "9.6.7", - "9.4.8", - "9.2.8", - "9.0.2", - "8.10.7", - "8.8.4", - ] + ghc: ${{ fromJSON(needs.config.outputs.GHC_FOR_VALIDATE_ALL) }} exclude: # Throws fatal "cabal-tests.exe: fd:8: hGetLine: end of file" exception # even with --io-manager=native @@ -100,216 +135,32 @@ jobs: 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 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 + - run: ${{ needs.config.outputs.env }} - - 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 + - uses: actions/checkout@v4 - # 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 + - uses: ./.github/actions/validate-build 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. - + shell: ${{ matrix.sys.shell }} + ghc: ${{ matrix.ghc }} + allow-newer: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.allow_newer || '' }} + constraints: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.constraints || '' }} + + # We never validate this. We would probably have caught https://gitlab.haskell.org/ghc/ghc/-/issues/25087 + # if we had. (I'll do this in Tier 2 unless someone thinks there's a good reason to do it here; the need + # for a container means it can't be shared reasonably unless everything is changed to use one like + # https://github.com/haskell/cabal/pull/9437 does in its release workflow.) build-alpine: name: Build statically linked using alpine runs-on: ubuntu-latest container: "alpine:3.19" + needs: config steps: + - run: ${{ needs.config.outputs.env }} + shell: sh + - name: Install extra dependencies shell: sh run: | @@ -324,181 +175,207 @@ jobs: - 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 + - uses: ./.github/actions/validate-build with: - ghc-version: ${{ env.GHC_FOR_RELEASE }} - cabal-version: latest # latest is mandatory for cabal-testsuite, see https://github.com/haskell/cabal/issues/8133 + ghc: ${{ env.GHC_FOR_RELEASE }} + static: 'true' + allow-newer: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.allow_newer || '' }} + constraints: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.constraints || '' }} + + # Run everything except cli-suite, which takes longer than the rest combined. + validate-tests: + name: Validate tests ${{ matrix.sys.os }} ghc-${{ matrix.ghc }} + runs-on: ${{ matrix.sys.os }} + needs: [config, validate-build] + 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 } + ghc: ${{ fromJSON(needs.config.outputs.GHC_FOR_VALIDATE_ALL) }} + exclude: + - sys: + { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } + ghc: "9.0.2" + - sys: + { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } + ghc: "8.10.7" + - sys: + { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } + ghc: "8.8.4" + - 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: + - run: ${{ needs.config.outputs.env }} + + - uses: actions/checkout@v4 - # See the following link for a breakdown of the following step - # https://github.com/haskell/actions/issues/7#issuecomment-745697160 - - uses: actions/cache@v4 + - uses: ./.github/actions/cabal-setup 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 + shell: ${{ matrix.sys.shell }} + ghc: ${{ matrix.ghc }} + allow-newer: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.allow_newer || '' }} + constraints: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.constraints || '' }} + + # Note: we rely on cabal and the test suites being already cached here. If they aren't, this + # may build them incorrectly and then possibly fail. (`cabal-setup` loads the cache for us.) + # @@@ GHA may go back to truncating caches, so this should be rewritten to use artifacts + - run: | + echo ::group::lib-tests + sh validate.sh $FLAGS -s lib-tests + echo ::group::lib-suite + sh validate.sh $FLAGS -s lib-suite + echo ::group::cli-tests + sh validate.sh $FLAGS -s cli-tests + + # Run just cli-suite. + validate-cli: + name: Validate CLI ${{ matrix.sys.os }} ghc-${{ matrix.ghc }} + runs-on: ${{ matrix.sys.os }} + needs: [config, validate-build] + 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 } + ghc: ${{ fromJSON(needs.config.outputs.GHC_FOR_VALIDATE_ALL) }} + exclude: + - sys: + { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } + ghc: "9.0.2" + - sys: + { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } + ghc: "8.10.7" + - sys: + { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } + ghc: "8.8.4" + - 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 }} - - name: Build - run: sh validate.sh $FLAGS -s build + steps: + - run: ${{ needs.config.outputs.env }} - - 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 + - uses: actions/checkout@v4 + + - uses: ./.github/actions/cabal-setup with: - name: cabal-${{ runner.os }}-static-x86_64 - path: ${{ env.CABAL_EXEC_TAR }} + shell: ${{ matrix.sys.shell }} + ghc: ${{ matrix.ghc }} + allow-newer: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.allow_newer || '' }} + constraints: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.constraints || '' }} - # 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 }} + - run: | + sh validate.sh $FLAGS -s cli-suite + + validate-solver: + name: Validate solver ${{ matrix.sys.os }} ghc-${{ needs.config.outputs.GHC_FOR_SOLVER_BENCHMARKS }} runs-on: ${{ matrix.sys.os }} - needs: validate + needs: [config, validate-build] 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 } - # 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" + - run: ${{ needs.config.outputs.env }} - uses: actions/checkout@v4 - - uses: haskell-actions/setup@v2 - id: setup-haskell + - uses: ./.github/actions/cabal-setup with: - ghc-version: ${{ matrix.ghc }} - cabal-version: latest # default, we are not using it in this job + shell: ${{ matrix.sys.shell }} + ghc: ${{ needs.config.outputs.GHC_FOR_SOLVER_BENCHMARKS }} + allow-newer: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.allow_newer || '' }} + constraints: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.constraints || '' }} - - name: Download cabal executable from workflow artifacts - uses: actions/download-artifact@v4 - with: - name: cabal-${{ runner.os }}-${{ env.CABAL_ARCH }} - path: cabal-head + - run: | + echo ::group::solver-benchmarks-tests + sh validate.sh $FLAGS -s solver-benchmarks-tests + echo ::group::solver-benchmarks-run + sh validate.sh $FLAGS -s solver-benchmarks-run - - name: Untar the cabal executable - run: tar -xzf "./cabal-head/cabal-head-${{ runner.os }}-$CABAL_ARCH.tar.gz" -C cabal-head + validate-old-ghcs: + name: Validate old ghcs ubuntu-22.04 ${{ matrix.extra-ghc }} + runs-on: ubuntu-22.04 + needs: [config, validate-build] - # 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 + strategy: + matrix: + extra-ghc: ${{ fromJSON(needs.config.outputs.GHC_FOR_VALIDATE_OLD) }} + fail-fast: false - prerelease-head: - name: Create a GitHub prerelease with the binary artifacts - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' - permissions: - contents: write + steps: + - run: ${{ needs.config.outputs.env }} + shell: bash - # IMPORTANT! Any job added to the workflow should be added here too - needs: [validate, validate-old-ghcs, build-alpine, dogfooding] + - uses: actions/checkout@v4 - steps: - - uses: actions/download-artifact@v4 + - uses: ./.github/actions/validate-old with: - pattern: cabal-* - path: binaries - merge-multiple: true + shell: bash + ghc: ${{ needs.config.outputs.GHC_FOR_RELEASE }} + extra-ghc: ${{ matrix.extra-ghc }} - - name: (Re)Create GitHub prerelease - uses: andelf/nightly-release@main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: cabal-head - name: cabal-head - prerelease: true - files: "binaries/cabal-*" + # The previous jobs use a released version of cabal to build cabal HEAD itself + # This one uses the cabal HEAD generated executable in `validate-build` + # to build itself again, as sanity check + dogfooding: + name: Dogfooding ${{ matrix.sys.os }} ghc-${{ needs.config.outputs.GHC_FOR_RELEASE }} + runs-on: ${{ matrix.sys.os }} + needs: [config, validate-build] + 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 } + fail-fast: false + defaults: + run: + shell: ${{ matrix.sys.shell }} - 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 + steps: + - run: ${{ needs.config.outputs.env }} - # IMPORTANT! Any job added to the workflow should be added here too - needs: [validate, validate-old-ghcs, build-alpine, dogfooding] + - uses: actions/checkout@v4 - 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 - - - name: (Re)Create GitHub prerelease - uses: andelf/nightly-release@main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: cabal-lts-head - name: cabal-lts-head - prerelease: true - files: "binaries/cabal-*" + - uses: ./.github/actions/dogfooding + with: + ghc: ${{ needs.config.outputs.GHC_FOR_RELEASE }} + shell: ${{ matrix.sys.shell }} + allow-newer: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.allow_newer || '' }} + constraints: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.constraints || '' }} # We use this job as a summary of the workflow # It will fail if any of the previous jobs does @@ -509,7 +386,7 @@ jobs: 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] + needs: [validate-build, validate-tests, validate-cli, validate-solver, validate-old-ghcs, build-alpine, dogfooding] steps: - run: | diff --git a/Makefile b/Makefile index e488ce17cf2..8a5a1154cfd 100644 --- a/Makefile +++ b/Makefile @@ -274,7 +274,8 @@ bootstrap-json-%: phony cd bootstrap && cabal run -v0 cabal-bootstrap-gen -- linux-$*.plan.json \ | python3 -m json.tool > linux-$*.json -BOOTSTRAP_GHC_VERSIONS := 9.2.8 9.4.8 9.6.7 9.8.4 9.10.2 9.12.2 +# extract bootstrap ghc versions from .github/config.yml +BOOTSTRAP_GHC_VERSIONS != sed -e '/^GHC_FOR_VALIDATE: /!d' -e 's/^.*: .\["\(.*\)"\].$$/\1/' -e 's/", "/ /g' <.github/config.yml .PHONY: bootstrap-jsons bootstrap-jsons: $(BOOTSTRAP_GHC_VERSIONS:%=bootstrap-json-%)