From 4d0d7266cdece6449199e3b6e146dc3bb79aceab Mon Sep 17 00:00:00 2001
From: Julian Ospald <hasufell@posteo.de>
Date: Mon, 13 Nov 2023 17:40:07 +0800
Subject: [PATCH] Migrate release CI back to github

---
 .cirrus.yml                                   |  31 ++
 .github/scripts/brew.sh                       |  28 ++
 .github/scripts/build.sh                      |  66 +++
 .github/scripts/common.sh                     | 110 +++++
 .github/scripts/env.sh                        |  42 ++
 .github/workflows/release.yaml                | 422 ++++++++++++++++++
 .github/workflows/validate.yml                | 160 +------
 .../cabal-install-solver.cabal                |   2 +-
 cabal-testsuite/cabal-testsuite.cabal         |   2 +-
 cabal.release.project                         |  19 +-
 cabal.validate.project                        |  14 +
 scripts/release/create-yaml-snippet.sh        | 132 ++++++
 scripts/release/download-gh-artifacts.sh      |  32 ++
 13 files changed, 898 insertions(+), 162 deletions(-)
 create mode 100644 .cirrus.yml
 create mode 100644 .github/scripts/brew.sh
 create mode 100644 .github/scripts/build.sh
 create mode 100644 .github/scripts/common.sh
 create mode 100644 .github/scripts/env.sh
 create mode 100644 .github/workflows/release.yaml
 create mode 100755 scripts/release/create-yaml-snippet.sh
 create mode 100755 scripts/release/download-gh-artifacts.sh

diff --git a/.cirrus.yml b/.cirrus.yml
new file mode 100644
index 00000000000..624088f2a04
--- /dev/null
+++ b/.cirrus.yml
@@ -0,0 +1,31 @@
+# release CI for FreeBSD
+compute_engine_instance:
+  image_project: freebsd-org-cloud-dev
+  image: family/freebsd-13-2
+  platform: freebsd
+  disk: 100 # Gb
+
+build_task:
+  timeout_in: 120m
+  only_if: $CIRRUS_TAG != ''
+  env:
+    ADD_CABAL_ARGS: "--enable-split-sections"
+    ARCH: 64
+    ARTIFACT: "x86_64-portbld-freebsd"
+    CIRRUS_CLONE_SUBMODULES: true
+    DISTRO: na
+    GHC_VERSION: 9.2.8
+    GITHUB_WORKSPACE: ${CIRRUS_WORKING_DIR}
+    RUNNER_OS: FreeBSD
+    TARBALL_EXT: tar.xz
+    TZ: Asia/Singapore
+  install_script:
+    - sed -i.bak -e 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
+    - pkg install -y ghc hs-cabal-install git bash misc/compat10x misc/compat11x misc/compat12x gmake llvm14 patchelf tree gmp libiconv
+  script:
+    - tzsetup Etc/GMT
+    - adjkerntz -a
+    - bash .github/scripts/build.sh
+  binaries_artifacts:
+    path: "out/*"
+
diff --git a/.github/scripts/brew.sh b/.github/scripts/brew.sh
new file mode 100644
index 00000000000..e05ffb7421c
--- /dev/null
+++ b/.github/scripts/brew.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+set -eux
+
+# shellcheck disable=SC1091
+. .github/scripts/env.sh
+
+if [ -e "$HOME/.brew" ] ; then
+	(
+	cd "$HOME/.brew"
+	git fetch --depth 1
+	git reset --hard origin/master
+	)
+else
+	git clone --depth=1 https://github.com/Homebrew/brew "$HOME/.brew"
+fi
+export PATH="$HOME/.brew/bin:$HOME/.brew/sbin:$PATH"
+
+mkdir -p "$CI_PROJECT_DIR/.brew_cache"
+export HOMEBREW_CACHE="$CI_PROJECT_DIR/.brew_cache"
+mkdir -p "$CI_PROJECT_DIR/.brew_logs"
+export HOMEBREW_LOGS="$CI_PROJECT_DIR/.brew_logs"
+mkdir -p /private/tmp/.brew_tmp
+export HOMEBREW_TEMP=/private/tmp/.brew_tmp
+
+#brew update
+brew install ${1+"$@"}
+
diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh
new file mode 100644
index 00000000000..556e1af472c
--- /dev/null
+++ b/.github/scripts/build.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+set -eux
+
+# shellcheck disable=SC1091
+. .github/scripts/env.sh
+# shellcheck disable=SC1091
+. .github/scripts/common.sh
+
+uname -a
+uname -p
+uname
+pwd
+env
+
+# ensure ghcup
+install_ghcup
+
+# build
+ghcup install ghc "${GHC_VERSION}"
+ghcup set ghc "${GHC_VERSION}"
+sed -i.bak -e '/DELETE MARKER FOR CI/,/END DELETE/d' cabal.project # see comment in cabal.project
+ecabal update
+ecabal user-config diff
+ecabal user-config init -f
+"ghc-${GHC_VERSION}" --info
+"ghc" --info
+
+# https://github.com/haskell/cabal/issues/7313#issuecomment-811851884
+if [ "$(getconf LONG_BIT)" == "32" ] || [ "${DISTRO}" == "CentOS" ] ; then
+    echo 'constraints: lukko -ofd-locking' >> cabal.release.project.local
+fi
+
+# shellcheck disable=SC2206
+args=(
+    -w "ghc-$GHC_VERSION"
+    --disable-profiling
+    --enable-executable-stripping
+    --project-file=cabal.release.project
+    ${ADD_CABAL_ARGS}
+)
+
+run cabal v2-build "${args[@]}" cabal-install
+
+mkdir -p "$CI_PROJECT_DIR/out"
+# shellcheck disable=SC2154
+cp "$(cabal list-bin "${args[@]}" cabal-install:exe:cabal)" "$CI_PROJECT_DIR/out/cabal$ext"
+cp dist-newstyle/cache/plan.json "$CI_PROJECT_DIR/out/plan.json"
+cd "$CI_PROJECT_DIR/out/"
+
+# create tarball/zip
+TARBALL_PREFIX="cabal-install-$("$CI_PROJECT_DIR/out/cabal" --numeric-version)"
+case "${TARBALL_EXT}" in
+    zip)
+        zip "${TARBALL_PREFIX}-${ARTIFACT}.${TARBALL_EXT}" "cabal${ext}" plan.json
+        ;;
+    tar.xz)
+        tar caf "${TARBALL_PREFIX}-${ARTIFACT}.${TARBALL_EXT}" "cabal${ext}" plan.json
+        ;;
+    *)
+        fail "Unknown TARBALL_EXT: ${TARBALL_EXT}"
+        ;;
+esac
+
+rm cabal plan.json
+
diff --git a/.github/scripts/common.sh b/.github/scripts/common.sh
new file mode 100644
index 00000000000..cff91ea6bb5
--- /dev/null
+++ b/.github/scripts/common.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+
+# shellcheck disable=SC1091
+. .github/scripts/env.sh
+
+# Colors
+RED="0;31"
+LT_BROWN="1;33"
+LT_BLUE="1;34"
+
+ecabal() {
+	cabal "$@"
+}
+
+nonfatal() {
+	"$@" || "$* failed"
+}
+
+sha_sum() {
+	if [ "${RUNNER_OS}" = "FreeBSD" ] ; then
+		sha256 "$@"
+	else
+		sha256sum "$@"
+	fi
+}
+
+git_describe() {
+	git config --global --get-all safe.directory | grep '^\*$' || git config --global --add safe.directory "*"
+	git describe --always
+}
+
+install_ghcup() {
+	# find "$GHCUP_INSTALL_BASE_PREFIX"
+	mkdir -p "$GHCUP_BIN"
+	mkdir -p "$GHCUP_BIN"/../cache
+
+	if [ "${RUNNER_OS}" = "FreeBSD" ] ; then
+		curl -o ghcup https://downloads.haskell.org/ghcup/tmp/x86_64-portbld-freebsd-ghcup-0.1.18.1
+		chmod +x ghcup
+		mv ghcup "$HOME/.local/bin/ghcup"
+	else
+		curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_MINIMAL=1 sh
+		source "$(dirname "${GHCUP_BIN}")/env"
+		ghcup install cabal --set "${BOOTSTRAP_HASKELL_CABAL_VERSION}"
+	fi
+}
+
+strip_binary() {
+	(
+	set -e
+	local binary=$1
+	case "$(uname -s)" in
+		"Darwin"|"darwin")
+			;;
+		MSYS_*|MINGW*)
+			;;
+		*)
+			strip -s "${binary}"
+		   ;;
+   esac
+	)
+}
+
+# GitLab Pipelines log section delimiters
+# https://gitlab.com/gitlab-org/gitlab-foss/issues/14664
+start_section() {
+  name="$1"
+  echo -e "section_start:$(date +%s):$name\015\033[0K"
+}
+
+end_section() {
+  name="$1"
+  echo -e "section_end:$(date +%s):$name\015\033[0K"
+}
+
+echo_color() {
+  local color="$1"
+  local msg="$2"
+  echo -e "\033[${color}m${msg}\033[0m"
+}
+
+error() { echo_color "${RED}" "$1"; }
+warn() { echo_color "${LT_BROWN}" "$1"; }
+info() { echo_color "${LT_BLUE}" "$1"; }
+
+fail() { error "error: $1"; exit 1; }
+
+run() {
+  info "Running $*..."
+  "$@" || ( error "$* failed"; return 1; )
+}
+
+emake() {
+	if command -v gmake >/dev/null 2>&1 ; then
+		gmake "$@"
+	else
+		make "$@"
+	fi
+}
+
+mktempdir() {
+	case "$(uname -s)" in
+		"Darwin"|"darwin")
+			mktemp -d -t cabal_ci.XXXXXXX
+			;;
+		*)
+			mktemp -d
+			;;
+	esac
+}
diff --git a/.github/scripts/env.sh b/.github/scripts/env.sh
new file mode 100644
index 00000000000..79acb00b474
--- /dev/null
+++ b/.github/scripts/env.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+mkdir -p "$HOME"/.local/bin
+
+if [ "${RUNNER_OS}" = "Windows" ] ; then
+	ext=".exe"
+else
+	# shellcheck disable=SC2034
+	ext=''
+fi
+
+export PATH="$HOME/.local/bin:$PATH"
+
+export BOOTSTRAP_HASKELL_NONINTERACTIVE=1
+export BOOTSTRAP_HASKELL_CABAL_VERSION="${CABAL_VER:-3.12.1.0}"
+export BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG=no
+export BOOTSTRAP_HASKELL_INSTALL_NO_STACK=yes
+export BOOTSTRAP_HASKELL_ADJUST_BASHRC=1
+
+if [ "${RUNNER_OS}" = "freebsd" ] ; then
+    export RUNNER_OS=FreeBSD
+fi
+
+if [ "${RUNNER_OS}" = "Windows" ] ; then
+    # on windows use pwd to get unix style path
+    CI_PROJECT_DIR="$(pwd)"
+    export CI_PROJECT_DIR
+    export GHCUP_INSTALL_BASE_PREFIX="/c"
+    export GHCUP_BIN="$GHCUP_INSTALL_BASE_PREFIX/ghcup/bin"
+    export PATH="$GHCUP_BIN:$PATH"
+    export CABAL_DIR="C:\\Users\\runneradmin\\AppData\\Roaming\\cabal"
+else
+    export CI_PROJECT_DIR="${GITHUB_WORKSPACE}"
+    export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
+    export GHCUP_BIN="$GHCUP_INSTALL_BASE_PREFIX/.ghcup/bin"
+    export PATH="$GHCUP_BIN:$PATH"
+    export CABAL_DIR="$CI_PROJECT_DIR/cabal"
+    export CABAL_CACHE="$CI_PROJECT_DIR/cabal-cache"
+fi
+
+export DEBIAN_FRONTEND=noninteractive
+export TZ=Asia/Singapore
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 00000000000..f3db33b7c7b
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,422 @@
+name: Build and release
+
+on:
+  push:
+    tags:
+      - 'cabal-install-*'
+
+jobs:
+  build-linux:
+    name: Build linux binaries
+    runs-on: ubuntu-latest
+    env:
+      TARBALL_EXT: tar.xz
+      ARCH: 64
+      DEBIAN_FRONTEND: noninteractive
+      TZ: Asia/Singapore
+      GHC_VERSION: 9.8.2
+    strategy:
+      fail-fast: false
+      matrix:
+        platform: [ { image: "debian:10"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses5 libtinfo5 patchelf"
+                    , DISTRO: "Debian"
+                    , ARTIFACT: "x86_64-linux-deb10"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "debian:11"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses5 libtinfo5 patchelf"
+                    , DISTRO: "Debian"
+                    , ARTIFACT: "x86_64-linux-deb11"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "debian:12"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses5 libtinfo5 patchelf"
+                    , DISTRO: "Debian"
+                    , ARTIFACT: "x86_64-linux-deb12"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "ubuntu:20.04"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses5 libtinfo5 patchelf"
+                    , DISTRO: "Ubuntu"
+                    , ARTIFACT: "x86_64-linux-ubuntu20.04"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "ubuntu:22.04"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses5 libtinfo5 patchelf"
+                    , DISTRO: "Ubuntu"
+                    , ARTIFACT: "x86_64-linux-ubuntu22.04"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "ubuntu:24.04"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses6 libtinfo6 patchelf"
+                    , DISTRO: "Ubuntu"
+                    , ARTIFACT: "x86_64-linux-ubuntu24.04"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "linuxmintd/mint20.3-amd64"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses5 libtinfo5 patchelf"
+                    , DISTRO: "Mint"
+                    , ARTIFACT: "x86_64-linux-mint20"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "linuxmintd/mint21.3-amd64"
+                    , installCmd: "apt-get update && apt-get install -y"
+                    , toolRequirements: "libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev libbz2-dev git wget lsb-release software-properties-common gnupg2 apt-transport-https gcc autoconf automake build-essential curl ghc gzip libffi-dev libncurses-dev libncurses5 libtinfo5 patchelf"
+                    , DISTRO: "Mint"
+                    , ARTIFACT: "x86_64-linux-mint21"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "fedora:33"
+                    , installCmd: "dnf install -y"
+                    , toolRequirements: "autoconf automake binutils bzip2 coreutils curl elfutils-devel elfutils-libs findutils gcc gcc-c++ git gmp gmp-devel jq lbzip2 make ncurses ncurses-compat-libs ncurses-devel openssh-clients patch perl pxz python3 sqlite sudo wget which xz zlib-devel patchelf"
+                    , DISTRO: "Fedora"
+                    , ARTIFACT: "x86_64-linux-fedora33"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "fedora:37"
+                    , installCmd: "dnf install -y"
+                    , toolRequirements: "autoconf automake binutils bzip2 coreutils curl elfutils-devel elfutils-libs findutils gcc gcc-c++ git gmp gmp-devel jq lbzip2 make ncurses ncurses-compat-libs ncurses-devel openssh-clients patch perl pxz python3 sqlite sudo wget which xz zlib-devel patchelf"
+                    , DISTRO: "Fedora"
+                    , ARTIFACT: "x86_64-linux-fedora37"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "rockylinux:8"
+                    , installCmd: "yum -y install epel-release && yum install -y --allowerasing"
+                    , toolRequirements: "autoconf automake binutils bzip2 coreutils curl elfutils-devel elfutils-libs findutils gcc gcc-c++ git gmp gmp-devel jq lbzip2 make ncurses ncurses-compat-libs ncurses-devel openssh-clients patch perl pxz python3 sqlite sudo wget which xz zlib-devel patchelf"
+                    , DISTRO: "Unknown"
+                    , ARTIFACT: "x86_64-linux-rocky8"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "rockylinux:9"
+                    , installCmd: "yum -y install epel-release && yum install -y --allowerasing"
+                    , toolRequirements: "autoconf automake binutils bzip2 coreutils curl elfutils-devel elfutils-libs findutils gcc gcc-c++ git gmp gmp-devel jq lbzip2 make ncurses ncurses-compat-libs ncurses-devel openssh-clients patch perl pxz python3 sqlite sudo wget which xz zlib-devel patchelf"
+                    , DISTRO: "Unknown"
+                    , ARTIFACT: "x86_64-linux-rocky9"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "alpine:latest"
+                    , installCmd: "apk update && apk add"
+                    , toolRequirements: "binutils-gold curl gcc g++ gmp-dev libc-dev libffi-dev make musl-dev ncurses-dev perl tar xz autoconf automake bzip2 coreutils elfutils-dev findutils git jq bzip2-dev  patch  python3 sqlite sudo wget which zlib-dev patchelf zlib zlib-dev zlib-static"
+                    , DISTRO: "Unknown"
+                    , ARTIFACT: "x86_64-linux-unknown"
+                    , ADD_CABAL_ARGS: "--enable-split-sections --enable-executable-static"
+                    },
+                    { image: "alpine:3.12"
+                    , installCmd: "apk update && apk add"
+                    , toolRequirements: "binutils-gold curl gcc g++ gmp-dev libc-dev libffi-dev make musl-dev ncurses-dev perl tar xz autoconf automake bzip2 coreutils elfutils-dev findutils git jq bzip2-dev  patch  python3 sqlite sudo wget which zlib-dev patchelf zlib zlib-dev zlib-static"
+                    , DISTRO: "Unknown"
+                    , ARTIFACT: "x86_64-linux-alpine312"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "alpine:3.19"
+                    , installCmd: "apk update && apk add"
+                    , toolRequirements: "binutils-gold curl gcc g++ gmp-dev libc-dev libffi-dev make musl-dev ncurses-dev perl tar xz autoconf automake bzip2 coreutils elfutils-dev findutils git jq bzip2-dev  patch  python3 sqlite sudo wget which zlib-dev patchelf zlib zlib-dev zlib-static"
+                    , DISTRO: "Unknown"
+                    , ARTIFACT: "x86_64-linux-alpine319"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    },
+                    { image: "ghcr.io/void-linux/void-glibc:latest"
+                    , installCmd: "xbps-install -Sy"
+                    , toolRequirements: "ncurses-libtinfo-libs autoconf automake binutils bzip2 coreutils curl elfutils-devel elfutils findutils gcc gmp gmp-devel jq lbzip2 make ncurses ncurses-devel openssh patch perl python3 sqlite sudo wget which xz tar zlib-devel patchelf"
+                    , DISTRO: "Unknown"
+                    , ARTIFACT: "x86_64-linux-void-glibc"
+                    , ADD_CABAL_ARGS: "--enable-split-sections"
+                    }
+                  ]
+    container:
+      image: ${{ matrix.platform.image }}
+    steps:
+      - name: Install requirements
+        shell: sh
+        run: |
+          ${{ matrix.platform.installCmd }} curl bash git ${{ matrix.platform.toolRequirements }}
+
+      - uses: actions/checkout@v4
+
+      - name: Run build
+        run: |
+          bash .github/scripts/build.sh
+
+        env:
+          ARTIFACT: ${{ matrix.platform.ARTIFACT }}
+          DISTRO: ${{ matrix.platform.DISTRO }}
+          ADD_CABAL_ARGS: ${{ matrix.platform.ADD_CABAL_ARGS }}
+
+      - if: always()
+        name: Upload artifact
+        uses: actions/upload-artifact@v4
+        with:
+          if-no-files-found: error
+          retention-days: 2
+          name: artifacts-${{ matrix.platform.ARTIFACT }}
+          path: |
+            ./out/*
+
+  build-linux-32bit:
+    name: Build linux binaries (32bit)
+    runs-on: ubuntu-latest
+    env:
+      TARBALL_EXT: tar.xz
+      ARCH: 32
+      TZ: Asia/Singapore
+      GHC_VERSION: 9.8.2
+      DISTRO: "Unknown"
+      ARTIFACT: "i386-linux-unknown"
+      ADD_CABAL_ARGS: "--enable-split-sections --enable-executable-static"
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          submodules: 'true'
+
+      - name: Run build (32 bit linux)
+        uses: docker://hasufell/i386-alpine-haskell:3.12
+        with:
+          args: sh -c "apk update && apk add bash binutils-gold curl gcc g++ gmp-dev libc-dev libffi-dev make musl-dev ncurses-dev perl tar xz autoconf automake bzip2 coreutils elfutils-dev findutils git jq bzip2-dev  patch  python3 sqlite sudo wget which zlib-dev patchelf zlib zlib-dev zlib-static && bash .github/scripts/build.sh"
+
+      - if: always()
+        name: Upload artifact
+        uses: actions/upload-artifact@v4
+        with:
+          if-no-files-found: error
+          retention-days: 2
+          name: artifacts-32bit
+          path: |
+            ./out/*
+
+  build-arm:
+    name: Build ARM binary
+    runs-on: ${{ matrix.os }}
+    env:
+      TARBALL_EXT: tar.xz
+      ADD_CABAL_ARGS: ""
+      DEBIAN_FRONTEND: noninteractive
+      TZ: Asia/Singapore
+      ARCH: ARM64
+      DISTRO: Debian
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - os: [self-hosted, Linux, ARM64, maerwald]
+            ARCH: ARM
+            ARTIFACT: "armv7-linux-deb10"
+            GHC_VERSION: 9.2.8
+          - os: [self-hosted, Linux, ARM64, maerwald]
+            ARCH: ARM64
+            ARTIFACT: "aarch64-linux-deb10"
+            GHC_VERSION: 9.8.2
+    steps:
+      - name: git config
+        run: |
+          git config --global --get-all safe.directory | grep '^\*$' || git config --global --add safe.directory "*"
+        shell: bash
+
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - if: matrix.ARCH == 'ARM'
+        uses: docker://hasufell/arm32v7-debian-haskell:10
+        name: Run build (armv7 linux)
+        with:
+          args: bash .github/scripts/build.sh
+        env:
+          ARTIFACT: ${{ matrix.ARTIFACT }}
+          GHC_VERSION: ${{ matrix.GHC_VERSION }}
+
+      - if: matrix.ARCH == 'ARM64'
+        uses: docker://hasufell/arm64v8-debian-haskell:10
+        name: Run build (aarch64 linux)
+        with:
+          args: bash .github/scripts/build.sh
+        env:
+          ARTIFACT: ${{ matrix.ARTIFACT }}
+          GHC_VERSION: ${{ matrix.GHC_VERSION }}
+
+      - if: always()
+        name: Upload artifact
+        uses: actions/upload-artifact@v4
+        with:
+          if-no-files-found: error
+          retention-days: 2
+          name: artifacts-${{ matrix.ARTIFACT }}
+          path: |
+            ./out/*
+
+  build-mac-x86_64:
+    name: Build binary (Mac x86_64)
+    runs-on: macOS-13
+    env:
+      MACOSX_DEPLOYMENT_TARGET: 10.13
+      ADD_CABAL_ARGS: ""
+      ARTIFACT: "x86_64-apple-darwin"
+      ARCH: 64
+      TARBALL_EXT: tar.xz
+      DISTRO: na
+      GHC_VERSION: 9.8.2
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Run build
+        run: |
+          brew install coreutils tree
+          bash .github/scripts/build.sh
+
+      - if: always()
+        name: Upload artifact
+        uses: actions/upload-artifact@v4
+        with:
+          if-no-files-found: error
+          retention-days: 2
+          name: artifacts-mac-x86_64
+          path: |
+            ./out/*
+
+  build-mac-aarch64:
+    name: Build binary (Mac aarch64)
+    runs-on: [self-hosted, macOS, ARM64]
+    env:
+      MACOSX_DEPLOYMENT_TARGET: 10.13
+      ADD_CABAL_ARGS: ""
+      ARTIFACT: "aarch64-apple-darwin"
+      ARCH: ARM64
+      TARBALL_EXT: tar.xz
+      DISTRO: na
+      HOMEBREW_CHANGE_ARCH_TO_ARM: 1
+      GHC_VERSION: 9.8.2
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Run build
+        run: |
+          bash .github/scripts/brew.sh git coreutils autoconf automake tree
+          export PATH="$HOME/.brew/bin:$HOME/.brew/sbin:$PATH"
+          export LD=ld
+          bash .github/scripts/build.sh
+
+      - if: always()
+        name: Upload artifact
+        uses: actions/upload-artifact@v4
+        with:
+          if-no-files-found: error
+          retention-days: 2
+          name: artifacts-mac-aarch64
+          path: |
+            ./out/*
+
+  build-win:
+    name: Build binary (Win)
+    runs-on: windows-latest
+    env:
+      ADD_CABAL_ARGS: ""
+      ARTIFACT: "x86_64-mingw64"
+      ARCH: 64
+      TARBALL_EXT: "zip"
+      DISTRO: na
+      GHC_VERSION: 9.8.2
+    steps:
+      - name: install windows deps
+        shell: pwsh
+        run: |
+          C:\msys64\usr\bin\bash -lc "pacman --disable-download-timeout --noconfirm -Syuu"
+          C:\msys64\usr\bin\bash -lc "pacman --disable-download-timeout --noconfirm -Syuu"
+          C:\msys64\usr\bin\bash -lc "pacman --disable-download-timeout --noconfirm -S make mingw-w64-x86_64-clang curl autoconf mingw-w64-x86_64-pkgconf ca-certificates base-devel gettext autoconf make libtool automake python p7zip patch unzip zip git"
+          taskkill /F /FI "MODULES eq msys-2.0.dll"
+
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Run build (windows)
+        run: |
+          $env:CHERE_INVOKING = 1
+          $env:MSYS2_PATH_TYPE = "inherit"
+          $ErrorActionPreference = "Stop"
+          C:\msys64\usr\bin\bash -lc "bash .github/scripts/build.sh"
+        shell: pwsh
+
+      - if: always()
+        name: Upload artifact
+        uses: actions/upload-artifact@v4
+        with:
+          if-no-files-found: error
+          retention-days: 2
+          name: artifacts-win
+          path: |
+            ./out/*
+
+  build-freebsd-x86_64:
+    name: Build FreeBSD x86_64
+    runs-on: [self-hosted, FreeBSD, X64]
+    env:
+      ADD_CABAL_ARGS: ""
+      ARTIFACT: "x86_64-portbld-freebsd"
+      ARCH: 64
+      TARBALL_EXT: tar.xz
+      DISTRO: na
+      GHC_VERSION: 9.8.2
+      RUNNER_OS: FreeBSD
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Run build
+        run: |
+          sudo sed -i.bak -e 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
+          sudo pkg install -y ghc hs-cabal-install git bash misc/compat10x misc/compat11x misc/compat12x gmake llvm14 libiconv
+          sudo tzsetup Etc/GMT
+          sudo adjkerntz -a
+          bash .github/scripts/build.sh
+
+      - if: always()
+        name: Upload artifact
+        uses: actions/upload-artifact@v4
+        with:
+          if-no-files-found: error
+          retention-days: 2
+          name: artifacts-freebsd
+          path: |
+            ./out/*
+
+  release:
+    name: release
+    needs: ["build-linux", "build-linux-32bit", "build-arm", "build-mac-x86_64", "build-mac-aarch64", "build-win", "build-freebsd-x86_64"]
+    runs-on: ubuntu-latest
+    if: startsWith(github.ref, 'refs/tags/')
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Download artifacts
+        uses: actions/download-artifact@v4
+        with:
+          pattern: artifacts-*
+          merge-multiple: true
+
+      - name: Install requirements
+        run: |
+          sudo apt-get update && sudo apt-get install -y tar xz-utils
+        shell: bash
+
+      - name: build sdists
+        run: |
+          cabal sdist -o out all
+        shell: bash
+
+      - name: Release
+        uses: softprops/action-gh-release@v1
+        with:
+          draft: true
+          files: |
+            ./out/*
+
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index c346838f93a..b3dbdf88d28 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -1,10 +1,5 @@
 name: Validate
 
-# See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#concurrency.
-concurrency:
-  group: ${{ github.ref }}-${{ github.workflow }}
-  cancel-in-progress: true
-
 # Note: This workflow file contains the required job "Validate post job". We are using path filtering
 # here to ignore PRs which only change documentation. This can cause a problem, see the workflow file
 # "validate.skip.yml" for a description of the problem and the solution provided in that file.
@@ -24,19 +19,6 @@ on:
   release:
     types:
       - created
-  workflow_call:
-
-  # See https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#hackage-revisions
-  workflow_dispatch:
-    inputs:
-      allow-newer:
-        description: allow-newer line
-        required: false
-        type: string
-      constraints:
-        description: constraints line
-        required: false
-        type: string
 
 env:
   # We choose a stable ghc version across all os's
@@ -67,34 +49,7 @@ jobs:
         # If you remove something from here, then add it to the old-ghcs job.
         # Also a removed GHC from here means that we are actually dropping
         # support, so the PR *must* have a changelog entry.
-        ghc:
-          [
-            "9.10.1",
-            "9.8.2",
-            "9.6.4",
-            "9.4.8",
-            "9.2.8",
-            "9.0.2",
-            "8.10.7",
-            "8.8.4",
-          ]
-        exclude:
-          # Throws fatal "cabal-tests.exe: fd:8: hGetLine: end of file" exception
-          # even with --io-manager=native
-          - sys:
-              { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
-            ghc: "9.0.2"
-          # corrupts GHA cache or the fabric of reality itself, see https://github.com/haskell/cabal/issues/8356
-          - sys:
-              { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
-            ghc: "8.10.7"
-          # lot of segfaults caused by ghc bugs
-          - sys:
-              { os: windows-latest, shell: "C:/msys64/usr/bin/bash.exe -e {0}" }
-            ghc: "8.8.4"
-    defaults:
-      run:
-        shell: ${{ matrix.sys.shell }}
+        ghc: ["9.2.8"]
     steps:
       - name: Work around XDG directories existence (haskell-actions/setup#62)
         if: runner.os == 'macOS'
@@ -231,65 +186,6 @@ jobs:
         if: matrix.ghc == env.GHC_FOR_SOLVER_BENCHMARKS
         run: sh validate.sh $FLAGS -s solver-benchmarks-run
 
-  validate-old-ghcs:
-    name: Validate old ghcs ${{ matrix.extra-ghc }}
-    runs-on: ubuntu-22.04
-    needs: validate
-
-    strategy:
-      matrix:
-        extra-ghc:
-          ["8.4.4", "8.2.2", "8.0.2"]
-          ## GHC 7.10.3 does not install on ubuntu-22.04 with ghcup.
-          ## Older GHCs are not supported by ghcup in the first place.
-      fail-fast: false
-
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Install prerequisites for old GHCs
-        run: |
-          sudo apt-get update
-          sudo apt-get install libncurses5 libtinfo5
-
-      - name: Install extra compiler
-        run: ghcup install ghc ${{ matrix.extra-ghc }}
-
-      - name: GHCup logs
-        if: always()
-        run: cat /usr/local/.ghcup/logs/*
-
-      - name: Install primary compiler
-        uses: haskell-actions/setup@v2
-        id: setup-haskell
-        with:
-          ghc-version: ${{ env.GHC_FOR_RELEASE }}
-          cabal-version: latest
-
-      - name: GHC versions
-        run: |
-          ghc --version
-          "ghc-${{ matrix.extra-ghc }}" --version
-
-      # As we are reusing the cached build dir from the previous step
-      # the generated artifacts are available here,
-      # including the cabal executable and the test suite
-      - uses: actions/cache@v4
-        with:
-          path: |
-            ${{ steps.setup-haskell.outputs.cabal-store }}
-            dist-*
-          key: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-${{ github.sha }}
-          restore-keys: ${{ runner.os }}-${{ env.GHC_FOR_RELEASE }}-
-
-      - name: Validate build
-        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 }}"
-
   build-alpine:
     name: Build statically linked using alpine
     runs-on: ubuntu-latest
@@ -400,57 +296,3 @@ jobs:
       - name: Build using cabal HEAD
         run: sh validate.sh ${{ env.COMMON_FLAGS }} --with-cabal ./cabal-head/cabal -s build
 
-  prerelease-head:
-    name: Create a GitHub prerelease with the binary artifacts
-    runs-on: ubuntu-latest
-    if: github.ref == 'refs/heads/master'
-
-    # IMPORTANT! Any job added to the workflow should be added here too
-    needs: [validate, validate-old-ghcs, build-alpine, dogfooding]
-
-    steps:
-      - uses: actions/download-artifact@v4
-        with:
-          name: cabal-Windows-x86_64
-
-      - uses: actions/download-artifact@v4
-        with:
-          name: cabal-Linux-x86_64
-
-      - uses: actions/download-artifact@v4
-        with:
-          name: cabal-Linux-static-x86_64
-
-      - uses: actions/download-artifact@v4
-        with:
-          name: cabal-macOS-x86_64
-
-      - name: Create GitHub prerelease
-        uses: marvinpinto/action-automatic-releases@v1.2.1
-        with:
-          repo_token: ${{ secrets.GITHUB_TOKEN }}
-          automatic_release_tag: cabal-head
-          prerelease: true
-          title: cabal-head
-          files: |
-            cabal-head-Windows-x86_64.tar.gz
-            cabal-head-Linux-x86_64.tar.gz
-            cabal-head-Linux-static-x86_64.tar.gz
-            cabal-head-macOS-x86_64.tar.gz
-
-  # We use this job as a summary of the workflow
-  # It will fail if any of the previous jobs does
-  # This way we can use it exclusively in branch protection rules
-  # and abstract away the concrete jobs of the workflow, including their names
-  validate-post-job:
-    if: always()
-    name: Validate post job
-    runs-on: ubuntu-latest
-    # IMPORTANT! Any job added to the workflow should be added here too
-    needs: [validate, validate-old-ghcs, build-alpine, dogfooding]
-
-    steps:
-      - run: |
-          echo "jobs info: ${{ toJSON(needs) }}"
-      - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
-        run: exit 1
diff --git a/cabal-install-solver/cabal-install-solver.cabal b/cabal-install-solver/cabal-install-solver.cabal
index dbdba88bbc1..a7cbac19849 100644
--- a/cabal-install-solver/cabal-install-solver.cabal
+++ b/cabal-install-solver/cabal-install-solver.cabal
@@ -106,7 +106,7 @@ library
     , containers    >=0.5.6.2  && <0.8
     , edit-distance ^>= 0.2.2
     , directory     >= 1.3.7.0  && < 1.4
-    , filepath      ^>=1.4.0.0 || ^>=1.5.0.0
+    , filepath      >= 1.3.0.1  && < 1.6
     , mtl           >=2.0      && <2.4
     , network-uri   >= 2.6.0.2 && < 2.7
     , pretty        ^>=1.1
diff --git a/cabal-testsuite/cabal-testsuite.cabal b/cabal-testsuite/cabal-testsuite.cabal
index 731cd374821..110788e74e9 100644
--- a/cabal-testsuite/cabal-testsuite.cabal
+++ b/cabal-testsuite/cabal-testsuite.cabal
@@ -69,7 +69,7 @@ library
     , cryptohash-sha256     ^>= 0.11.101.0
     , directory             ^>= 1.2.0.1 || ^>= 1.3.0.0
     , exceptions            ^>= 0.10.0
-    , filepath              ^>= 1.3.0.1 || ^>= 1.4.0.0 || ^>= 1.5.0.0
+    , filepath              >= 1.3.0.1  && < 1.6
     , network-wait          ^>= 0.1.2.0 || ^>= 0.2.0.0
     , optparse-applicative  ^>= 0.14.3.0 || ^>=0.15.1.0 || ^>=0.16.0.0 || ^>= 0.17.0.0 || ^>= 0.18.1.0
     , process               ^>= 1.2.1.0 || ^>= 1.4.2.0 || ^>= 1.6.1.0
diff --git a/cabal.release.project b/cabal.release.project
index 676e9f68587..f3dcf54e63b 100644
--- a/cabal.release.project
+++ b/cabal.release.project
@@ -2,4 +2,21 @@ import: project-cabal/pkgs/cabal.config
 import: project-cabal/pkgs/install.config
 import: project-cabal/pkgs/tests.config
 
-index-state: hackage.haskell.org 2024-09-05T17:19:45Z
+constraints:
+  hashable -arch-native,
+  tar >= 0.6.2.0,
+  bzlib-conduit >= 0.3.0.3,
+  bz2 >= 1.0.1.1,
+  bzlib >= 0.5.2.0,
+  directory >= 1.3.8.3,
+  filepath == 1.4.101.0 || == 1.4.300.1 || >= 1.5.2.0
+
+-- https://github.com/haskell/ghcup-hs/issues/1107
+-- https://github.com/haskell/unix/pull/318
+if arch(arm) || arch(i386)
+  constraints: unix >= 2.8.6.0
+
+package zlib
+  flags: -pkg-config +bundled-c-zlib
+
+index-state: hackage.haskell.org 2024-12-21T16:10:10Z
diff --git a/cabal.validate.project b/cabal.validate.project
index 52c78411107..d7a17e6188f 100644
--- a/cabal.validate.project
+++ b/cabal.validate.project
@@ -7,3 +7,17 @@ tests: True
 write-ghc-environment-files: never
 program-options
   ghc-options: -Werror
+
+-- This project file is used to distribute the cabal-head binary,
+-- as such we cannot enable "-march=native".
+constraints:
+  hashable -arch-native,
+  tar >= 0.6.2.0,
+  bzlib-conduit >= 0.3.0.3,
+  bz2 >= 1.0.1.1,
+  bzlib >= 0.5.2.0,
+  directory >= 1.3.8.3,
+  filepath == 1.4.101.0 || == 1.4.300.1 || >= 1.5.2.0
+
+package zlib
+  flags: -pkg-config +bundled-c-zlib
diff --git a/scripts/release/create-yaml-snippet.sh b/scripts/release/create-yaml-snippet.sh
new file mode 100755
index 00000000000..9a9cee3e092
--- /dev/null
+++ b/scripts/release/create-yaml-snippet.sh
@@ -0,0 +1,132 @@
+#!/bin/bash
+
+set -eu
+set -o pipefail
+
+RELEASE=$1
+VERSION=${RELEASE#cabal-install-v}
+
+cd "gh-release-artifacts/cabal-${VERSION}"
+BASE_URL=https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal/$VERSION
+
+cat <<EOF > /dev/stdout
+    $VERSION:
+      viTags:
+        - Latest
+      viChangeLog: https://github.com/haskell/cabal/blob/master/release-notes/cabal-install-$VERSION.md
+      viArch:
+        A_64:
+          Linux_Debian:
+            '< 10': &cabal-${VERSION//./}-64-deb9
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-deb9.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-deb9.tar.xz" | awk '{ print $1 }')
+            '(>= 10 && < 11)': &cabal-${VERSION//./}-64-deb10
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-deb10.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-deb10.tar.xz" | awk '{ print $1 }')
+            '(>= 11 && < 12)': &cabal-${VERSION//./}-64-deb11
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-deb11.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-deb11.tar.xz" | awk '{ print $1 }')
+            '(>= 12 && < 13)': &cabal-${VERSION//./}-64-deb12
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-deb12.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-deb12.tar.xz" | awk '{ print $1 }')
+            unknown_versioning: *cabal-${VERSION//./}-64-deb12
+          Linux_Ubuntu:
+            '( >= 16 && < 19 )': &cabal-${VERSION//./}-64-ubuntu18
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-ubuntu18.04.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-ubuntu18.04.tar.xz" | awk '{ print $1 }')
+            '( >= 20 && < 22 )': &cabal-${VERSION//./}-64-ubuntu20
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-ubuntu20.04.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-ubuntu20.04.tar.xz" | awk '{ print $1 }')
+            '( >= 22 && < 24 )': &cabal-${VERSION//./}-64-ubuntu22
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-ubuntu22.04.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-ubuntu22.04.tar.xz" | awk '{ print $1 }')
+            '( >= 24 && < 25 )': &cabal-${VERSION//./}-64-ubuntu24
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-ubuntu24.04.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-ubuntu24.04.tar.xz" | awk '{ print $1 }')
+            unknown_versioning: *cabal-${VERSION//./}-64-ubuntu24
+          Linux_Mint:
+            '< 20': &cabal-${VERSION//./}-64-mint19
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-mint19.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-mint19.tar.xz" | awk '{ print $1 }')
+            '(>= 20 && < 21)': &cabal-${VERSION//./}-64-mint20
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-mint20.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-mint20.tar.xz" | awk '{ print $1 }')
+            '>= 21': &cabal-${VERSION//./}-64-mint21
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-mint21.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-mint21.tar.xz" | awk '{ print $1 }')
+            unknown_versioning: *cabal-${VERSION//./}-64-mint21
+          Linux_Fedora:
+            '< 33': &cabal-${VERSION//./}-64-fedora27
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-fedora27.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-fedora27.tar.xz" | awk '{ print $1 }')
+            '(>= 33 && < 37)': &cabal-${VERSION//./}-64-fedora33
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-fedora33.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-fedora33.tar.xz" | awk '{ print $1 }')
+            '>= 37': &cabal-${VERSION//./}-64-fedora37
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-fedora37.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-fedora37.tar.xz" | awk '{ print $1 }')
+            unknown_versioning: *cabal-${VERSION//./}-64-fedora37
+          Linux_CentOS:
+            '( >= 7 && < 8 )': &cabal-${VERSION//./}-64-centos
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-centos7.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-centos7.tar.xz" | awk '{ print $1 }')
+            unknown_versioning: *cabal-${VERSION//./}-64-centos
+          Linux_Rocky:
+            '( >= 8 && < 9 )': &cabal-${VERSION//./}-64-rocky8
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-rocky8.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-rocky8.tar.xz" | awk '{ print $1 }')
+            '( >= 9 )': &cabal-${VERSION//./}-64-rocky9
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-rocky9.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-rocky9.tar.xz" | awk '{ print $1 }')
+            unknown_versioning: *cabal-${VERSION//./}-64-rocky9
+          Linux_RedHat:
+            unknown_versioning: *cabal-${VERSION//./}-64-rocky8
+          Linux_UnknownLinux:
+            unknown_versioning: &cabal-${VERSION//./}-64-unknown
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-unknown.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-unknown.tar.xz" | awk '{ print $1 }')
+          Linux_Alpine:
+            '( >= 3.12 && < 3.19 )': &cabal-${VERSION//./}-64-alpine312
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-alpine312.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-alpine312.tar.xz" | awk '{ print $1 }')
+            '( >= 3.19 )': &cabal-${VERSION//./}-64-alpine319
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-linux-alpine319.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-linux-alpine319.tar.xz" | awk '{ print $1 }')
+            unknown_versioning: *cabal-${VERSION//./}-64-alpine319
+          Linux_Void:
+            unknown_versioning: *cabal-${VERSION//./}-64-unknown
+          Darwin:
+            unknown_versioning:
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-apple-darwin.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-apple-darwin.tar.xz" | awk '{ print $1 }')
+          Windows:
+            unknown_versioning:
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-mingw64.zip
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-mingw64.zip" | awk '{ print $1 }')
+          FreeBSD:
+            unknown_versioning:
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-x86_64-portbld-freebsd.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-x86_64-portbld-freebsd.tar.xz" | awk '{ print $1 }')
+        A_32:
+          Linux_UnknownLinux:
+            unknown_versioning: &cabal-${VERSION//./}-32-unknown
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-i386-linux-unknown.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-i386-linux-unknown.tar.xz" | awk '{ print $1 }')
+          Linux_Alpine:
+            unknown_versioning: *cabal-${VERSION//./}-32-unknown
+        A_ARM64:
+          Linux_UnknownLinux:
+            unknown_versioning:
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-aarch64-linux-deb10.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-aarch64-linux-deb10.tar.xz" | awk '{ print $1 }')
+          Darwin:
+            unknown_versioning:
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-aarch64-apple-darwin.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-aarch64-apple-darwin.tar.xz" | awk '{ print $1 }')
+        A_ARM:
+          Linux_UnknownLinux:
+            unknown_versioning:
+              dlUri: ${BASE_URL}/cabal-install-$VERSION-armv7-linux-deb10.tar.xz
+              dlHash: $(sha256sum "cabal-install-$VERSION-armv7-linux-deb10.tar.xz" | awk '{ print $1 }')
+EOF
+
diff --git a/scripts/release/download-gh-artifacts.sh b/scripts/release/download-gh-artifacts.sh
new file mode 100755
index 00000000000..38c57169187
--- /dev/null
+++ b/scripts/release/download-gh-artifacts.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+set -eu
+set -o pipefail
+
+RELEASE=$1
+VERSION=${RELEASE#cabal-install-v}
+SIGNER=$2
+
+echo "RELEASE: $RELEASE"
+echo "SIGNER: $SIGNER"
+
+for com in gh gpg curl sha256sum ; do
+	command -V ${com} >/dev/null 2>&1
+done
+
+[ ! -e "gh-release-artifacts/cabal-${VERSION}" ]
+
+mkdir -p "gh-release-artifacts/cabal-${VERSION}"
+
+git archive --format=tar.gz -o "gh-release-artifacts/cabal-${VERSION}/cabal-${VERSION}-src.tar.gz" --prefix="cabal-${VERSION}/" HEAD
+
+cd "gh-release-artifacts/cabal-${VERSION}"
+
+# github
+gh release download "$RELEASE"
+
+sha256sum ./* > SHA256SUMS
+gpg --detach-sign -u "${SIGNER}" SHA256SUMS
+
+gh release upload "$RELEASE" "cabal-${VERSION}-src.tar.gz" SHA256SUMS SHA256SUMS.sig
+