From 3c3a3d201fa3d66395cae4dab20f585e480b3a96 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 | 68 +++ .github/actions/cabal-setup/action.yml | 164 ++++++ .github/actions/dogfooding/action.yml | 61 +++ .github/actions/validate-build/action.yml | 102 ++++ .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, 789 insertions(+), 377 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..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-%)