name: 'Maximize build disk space' description: 'Maximize the available disk space for your build job' branding: icon: 'crop' color: 'orange' inputs: root-reserve-mb: description: 'Space to be left free on the root filesystem, in Megabytes.' required: false default: '1024' temp-reserve-mb: description: 'Space to be left free on the temp filesystem (/mnt), in Megabytes.' required: false default: '100' swap-size-mb: description: 'Swap space to create, in Megabytes.' required: false default: '4096' overprovision-lvm: description: | Create the LVM disk images as sparse files, making the space required for the LVM image files *appear* unused on the hosting volumes until actually allocated. Use with care, this can lead to surprising out-of-disk-space situations. You should prefer adjusting root-reserve-mb/temp-reserve-mb over using this option. required: false default: 'false' build-mount-path: description: 'Absolute path to the mount point where the build space will be available, defaults to $GITHUB_WORKSPACE if unset.' required: false build-mount-path-ownership: description: 'Ownership of the mount point path, defaults to standard "runner" user and group.' required: false default: 'runner:runner' pv-loop-path: description: 'Absolute file path for the LVM image created on the root filesystem, the default is usually fine.' required: false default: '/pv.img' tmp-pv-loop-path: description: 'Absolute file path for the LVM image created on the temp filesystem, the default is usually fine. Must reside on /mnt' required: false default: '/mnt/tmp-pv.img' remove-dotnet: description: 'Removes .NET runtime and libraries. (frees ~17 GB)' required: false default: 'false' remove-android: description: 'Removes Android SDKs and Tools. (frees ~11 GB)' required: false default: 'false' remove-haskell: description: 'Removes GHC (Haskell) artifacts. (frees ~2.7 GB)' required: false default: 'false' remove-codeql: description: 'Removes CodeQL Action Bundles. (frees ~5.4 GB)' required: false default: 'false' remove-docker-images: description: 'Removes cached Docker images. (frees ~3 GB)' required: false default: 'false' runs: using: "composite" steps: - name: Disk space report before modification shell: bash run: | echo "Memory and swap:" sudo free -h echo sudo swapon --show echo echo "Available storage:" sudo df -h echo - name: Maximize build disk space shell: bash run: | set -euo pipefail BUILD_MOUNT_PATH="${{ inputs.build-mount-path }}" if [[ -z "${BUILD_MOUNT_PATH}" ]]; then BUILD_MOUNT_PATH="${GITHUB_WORKSPACE}" fi echo "Arguments:" echo echo " Root reserve: ${{ inputs.root-reserve-mb }} MiB" echo " Temp reserve: ${{ inputs.temp-reserve-mb }} MiB" echo " Swap space: ${{ inputs.swap-size-mb }} MiB" echo " Overprovision LVM: ${{ inputs.overprovision-lvm }}" echo " Mount path: ${BUILD_MOUNT_PATH}" echo " Root PV loop path: ${{ inputs.pv-loop-path }}" echo " Temp PV loop path: ${{ inputs.tmp-pv-loop-path }}" echo -n " Removing: " if [[ ${{ inputs.remove-dotnet }} == 'true' ]]; then echo -n "dotnet " fi if [[ ${{ inputs.remove-android }} == 'true' ]]; then echo -n "android " fi if [[ ${{ inputs.remove-haskell }} == 'true' ]]; then echo -n "haskell " fi if [[ ${{ inputs.remove-codeql }} == 'true' ]]; then echo -n "codeql " fi if [[ ${{ inputs.remove-docker-images }} == 'true' ]]; then echo -n "docker " fi echo # store owner of $GITHUB_WORKSPACE in case the action deletes it WORKSPACE_OWNER="$(stat -c '%U:%G' "${GITHUB_WORKSPACE}")" # ensure mount path exists before the action sudo mkdir -p "${BUILD_MOUNT_PATH}" sudo find "${BUILD_MOUNT_PATH}" -maxdepth 0 ! -empty -exec echo 'WARNING: directory [{}] is not empty, data loss might occur. Content:' \; -exec ls -al "{}" \; echo "Removing unwanted software... " if [[ ${{ inputs.remove-dotnet }} == 'true' ]]; then sudo rm -rf /usr/share/dotnet fi if [[ ${{ inputs.remove-android }} == 'true' ]]; then sudo rm -rf /usr/local/lib/android fi if [[ ${{ inputs.remove-haskell }} == 'true' ]]; then sudo rm -rf /opt/ghc fi if [[ ${{ inputs.remove-codeql }} == 'true' ]]; then sudo rm -rf /opt/hostedtoolcache/CodeQL fi if [[ ${{ inputs.remove-docker-images }} == 'true' ]]; then sudo docker image prune --all --force fi echo "... done" VG_NAME=buildvg # github runners have an active swap file in /mnt/swapfile # we want to reuse the temp disk, so first unmount swap and clean the temp disk echo "Unmounting and removing swap file." sudo swapoff -a sudo rm -f /mnt/swapfile echo "Creating LVM Volume." echo " Creating LVM PV on root fs." # create loop pv image on root fs ROOT_RESERVE_KB=$(expr ${{ inputs.root-reserve-mb }} \* 1024) ROOT_FREE_KB=$(df --block-size=1024 --output=avail / | tail -1) ROOT_LVM_SIZE_KB=$(expr $ROOT_FREE_KB - $ROOT_RESERVE_KB) ROOT_LVM_SIZE_BYTES=$(expr $ROOT_LVM_SIZE_KB \* 1024) sudo touch "${{ inputs.pv-loop-path }}" && sudo fallocate -z -l "${ROOT_LVM_SIZE_BYTES}" "${{ inputs.pv-loop-path }}" export ROOT_LOOP_DEV=$(sudo losetup --find --show "${{ inputs.pv-loop-path }}") sudo pvcreate -f "${ROOT_LOOP_DEV}" # create pv on temp disk echo " Creating LVM PV on temp fs." TMP_RESERVE_KB=$(expr ${{ inputs.temp-reserve-mb }} \* 1024) TMP_FREE_KB=$(df --block-size=1024 --output=avail /mnt | tail -1) TMP_LVM_SIZE_KB=$(expr $TMP_FREE_KB - $TMP_RESERVE_KB) TMP_LVM_SIZE_BYTES=$(expr $TMP_LVM_SIZE_KB \* 1024) sudo touch "${{ inputs.tmp-pv-loop-path }}" && sudo fallocate -z -l "${TMP_LVM_SIZE_BYTES}" "${{ inputs.tmp-pv-loop-path }}" export TMP_LOOP_DEV=$(sudo losetup --find --show "${{ inputs.tmp-pv-loop-path }}") sudo pvcreate -f "${TMP_LOOP_DEV}" # create volume group from these pvs sudo vgcreate "${VG_NAME}" "${TMP_LOOP_DEV}" "${ROOT_LOOP_DEV}" echo "Recreating swap" # create and activate swap sudo lvcreate -L "${{ inputs.swap-size-mb }}M" -n swap "${VG_NAME}" sudo mkswap "/dev/mapper/${VG_NAME}-swap" sudo swapon "/dev/mapper/${VG_NAME}-swap" echo "Creating build volume" # create and mount build volume sudo lvcreate -l 100%FREE -n buildlv "${VG_NAME}" if [[ ${{ inputs.overprovision-lvm }} == 'true' ]]; then sudo mkfs.ext4 -m0 "/dev/mapper/${VG_NAME}-buildlv" else sudo mkfs.ext4 -Enodiscard -m0 "/dev/mapper/${VG_NAME}-buildlv" fi sudo mount "/dev/mapper/${VG_NAME}-buildlv" "${BUILD_MOUNT_PATH}" sudo chown -R "${{ inputs.build-mount-path-ownership }}" "${BUILD_MOUNT_PATH}" # if build mount path is a parent of $GITHUB_WORKSPACE, and has been deleted, recreate it if [[ ! -d "${GITHUB_WORKSPACE}" ]]; then sudo mkdir -p "${GITHUB_WORKSPACE}" sudo chown -R "${WORKSPACE_OWNER}" "${GITHUB_WORKSPACE}" fi - name: Disk space report after modification shell: bash run: | echo "Memory and swap:" sudo free -h echo sudo swapon --show echo echo "Available storage:" sudo df -h