From 362d05c9e9c03ce1e28861bcb6dda3243e913613 Mon Sep 17 00:00:00 2001 From: brandon s allbery kf8nh Date: Fri, 1 Nov 2024 19:27:14 -0400 Subject: [PATCH] attempt to use local actions Extremely experimental, and with all the limitations and restrictions I keep finding in GitHub Actions it'll probably fail in the messiest way it can. At present this is incomplete but sufficient to see if this has any chance of working to begin with. If it somehow does, I'll look into abstracting out the other sub-jobs, then making an overnight validate for Tier 2 platforms and probably a prerelease job (which would fix the recently revealed problem where if there is no need to rebase on merge, no prerelease is made). --- .github/README.md | 72 +++ .github/actions/cabal-setup/action.yml | 164 ++++++ .github/actions/dogfooding/action.yml | 61 +++ .github/actions/validate-build/action.yml | 101 ++++ .github/actions/validate-old/action.yml | 55 ++ .github/config.yml | 30 ++ .github/workflows/bootstrap.yml | 56 +- .github/workflows/validate.yml | 627 +++++++++------------- Makefile | 3 +- 9 files changed, 783 insertions(+), 386 deletions(-) create mode 100644 .github/README.md create mode 100644 .github/actions/cabal-setup/action.yml create mode 100644 .github/actions/dogfooding/action.yml create mode 100644 .github/actions/validate-build/action.yml create mode 100644 .github/actions/validate-old/action.yml create mode 100644 .github/config.yml diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 00000000000..3841e0dd563 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,72 @@ +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 + +You can use a manual dispatch on the validate workflow. It has two optional parameters: +- `allow-newer line` will add an `allow-newer:` entry to the project file. Don't include the prefix. +- `constraints line` will similarly add a `comnstraints:` entry. + +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..df889d922a0 --- /dev/null +++ b/.github/actions/validate-build/action.yml @@ -0,0 +1,101 @@ +# +# 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 + # - Make it available in the workflow to make easier testing it locally + - 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..213648525ac --- /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 (not typically needed here because this only runs on Ubuntu) +# + +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 16c7cfd45c0..df3812f9993 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@v5 + + - 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@v5 + - uses: haskell-actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index c28c5133b05..05843d0f866 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -9,8 +9,6 @@ on: push: branches: - master - # hardcoded LTS branch, change when new LTS released! - - '3.12' pull_request: release: types: @@ -29,47 +27,68 @@ 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. + # + # And GitHub documents that $GITHUB_ENV doesn't persist between jobs, only between + # steps. But somehow the `validate` job was getting it anyway, while `build-alpine` + # wasn't. + # + # I give up. Everything is now explicitly (GitHub hasn't invented iteration yet) listed + # as an output and (again explicitly) loaded into the environment in the first step of + # each job. Thanks, GitHub. (Some parts of it are not obviously used, because you need + # to dig around the local actions to see where other parts are used. $COMMON_FLAGS is + # one notable example.) + # + # Oh, and anchors aren't supported either, so yes, it has to be copy-pasted. Thanks again. + # (Actually, somehow GitHub missed making ${{ }} expansions illegal in `run`.) + 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_OLD: ${{ steps.conf.outputs['GHC_FOR_VALIDATE_OLD'] }} + 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_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@v5 -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 + + 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 strategy: fail-fast: false + # The matrix has to be duplicated in multiple places, because it never + # occurred to the YAML "programmers" at GitHub that it might be useful to + # refer to it in multiple jobs. 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-latest, shell: bash } - { os: macos-latest, shell: bash } - ghc: - [ - # IMPORTANT: If you remove a version from this list, then add it to the old-ghcs job below. - # Also a removed GHC from here means that we are actually dropping - # support, so the PR *must* have a changelog entry. - "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) }} exclude: # Throws fatal "cabal-tests.exe: fd:8: hGetLine: end of file" exception # even with --io-manager=native @@ -100,223 +119,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@v5 - - # 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 }}- - - - name: "Work around git problem https://bugs.launchpad.net/ubuntu/+source/git/+bug/1993586 (cabal PR #8546)" - # alternatively, use 'continue-on-error: true' - if: runner.os != 'Windows' - run: git config --global protocol.file.allow always - - # 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 ::group::Validate "$test" - sh validate.sh $FLAGS -s "$test" || rc=1 - echo End "$test" - echo ::endgroup:: - 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 }} - # latest/latest doesn't have the old packages the old ghcs depend on - runs-on: ubuntu-22.04 - needs: validate - - strategy: - matrix: - extra-ghc: - ["8.6.5", "8.4.4", "8.2.2", "8.0.2"] - ## GHC 7.10.3 does not install on ubuntu-latest with ghcup. - ## Older GHCs are not supported by ghcup in the first place. - fail-fast: false steps: - - uses: actions/checkout@v5 - - - 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/* + - run: ${{ needs.config.outputs.env }} - - 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@v5 - # 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.20" + needs: config steps: + - run: ${{ needs.config.outputs.env }} + shell: sh + - name: Install extra dependencies shell: sh run: | @@ -331,181 +159,212 @@ jobs: - uses: actions/checkout@v5 - # 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 || '' }} + + # First, apparently it is now necessary to split out _only_ `cli-suite`. (Because somehow that was supposed to make this "simpler") + # Second, supposedly I can use `include` to add in the `solver-benchmarks` tests, but if I do + # I get an incomprehensible error about `runs-on` being empty. + validate-tests: + name: Validate tests ${{ matrix.sys.os }} ghc-${{ matrix.ghc }} + runs-on: ${{ matrix.sys.os }} + needs: [config, validate-build] + strategy: + fail-fast: false + # sadly, `matrix` can't be copied and GHA doesn't yet support anchors (2nd half of 2025 supposedly) + matrix: + sys: + - { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" } + - { os: ubuntu-latest, shell: bash } + - { os: macos-latest, shell: bash } + ghc: ${{ fromJSON(needs.config.outputs.GHC_FOR_VALIDATE) }} + 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 }} - # 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: actions/checkout@v5 + + - 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 now truncates caches, so must rewrite using 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 + + validate-cli: + name: Validate CLI ${{ matrix.sys.os }} ghc-${{ matrix.ghc }} + runs-on: ${{ matrix.sys.os }} + needs: [config, validate-build] + strategy: + fail-fast: false + # another `matrix` instance to be replaced with an anchor when available + 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) }} + 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@v5 + + - 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-latest, shell: bash } + - { 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@v5 - - 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@v5 - 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@v5 - steps: - - uses: actions/download-artifact@v5 - with: - pattern: cabal-* - path: binaries - merge-multiple: true - - - name: (Re)Create GitHub prerelease - uses: andelf/nightly-release@main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: ./.github/actions/validate-old with: - tag_name: cabal-head - name: cabal-head - prerelease: true - files: "binaries/cabal-*" + shell: bash + ghc: ${{ needs.config.outputs.GHC_FOR_RELEASE }} + extra-ghc: ${{ matrix.extra-ghc }} - 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] + # 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: + # '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 } + fail-fast: false + defaults: + run: + shell: ${{ matrix.sys.shell }} steps: - - uses: actions/download-artifact@v5 - 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-*" + - run: ${{ needs.config.outputs.env }} + + - uses: actions/checkout@v5 + + - 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 @@ -516,7 +375,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 2c6ab052461..9b91b662e40 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-%)