| #!/bin/bash | |
| set -euo pipefail | |
| # Free disk space on Linux GitHub action runners | |
| # Script inspired by https://github.com/jlumbroso/free-disk-space | |
| isX86() { | |
| local arch | |
| arch=$(uname -m) | |
| if [ "$arch" = "x86_64" ]; then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| # Check if we're on a GitHub hosted runner. | |
| # In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted". | |
| isGitHubRunner() { | |
| # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string". | |
| if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| # print a line of the specified character | |
| printSeparationLine() { | |
| for ((i = 0; i < 80; i++)); do | |
| printf "%s" "$1" | |
| done | |
| printf "\n" | |
| } | |
| # compute available space | |
| # REF: https://unix.stackexchange.com/a/42049/60849 | |
| # REF: https://stackoverflow.com/a/450821/408734 | |
| getAvailableSpace() { | |
| df -a | awk 'NR > 1 {avail+=$4} END {print avail}' | |
| } | |
| # make Kb human readable (assume the input is Kb) | |
| # REF: https://unix.stackexchange.com/a/44087/60849 | |
| formatByteCount() { | |
| numfmt --to=iec-i --suffix=B --padding=7 "${1}000" | |
| } | |
| # macro to output saved space | |
| printSavedSpace() { | |
| # Disk space before the operation | |
| local before=${1} | |
| local title=${2:-} | |
| local after | |
| after=$(getAvailableSpace) | |
| local saved=$((after - before)) | |
| if [ "$saved" -lt 0 ]; then | |
| echo "::warning::Saved space is negative: $saved. Using '0' as saved space." | |
| saved=0 | |
| fi | |
| echo "" | |
| printSeparationLine "*" | |
| if [ -n "${title}" ]; then | |
| echo "=> ${title}: Saved $(formatByteCount "$saved")" | |
| else | |
| echo "=> Saved $(formatByteCount "$saved")" | |
| fi | |
| printSeparationLine "*" | |
| echo "" | |
| } | |
| # macro to print output of df with caption | |
| printDF() { | |
| local caption=${1} | |
| printSeparationLine "=" | |
| echo "${caption}" | |
| echo "" | |
| echo "$ df -h" | |
| echo "" | |
| df -h | |
| printSeparationLine "=" | |
| } | |
| removeUnusedFilesAndDirs() { | |
| local to_remove=( | |
| "/usr/share/java" | |
| ) | |
| if isGitHubRunner; then | |
| to_remove+=( | |
| "/usr/local/aws-sam-cli" | |
| "/usr/local/doc/cmake" | |
| "/usr/local/julia"* | |
| "/usr/local/lib/android" | |
| "/usr/local/share/chromedriver-"* | |
| "/usr/local/share/chromium" | |
| "/usr/local/share/cmake-"* | |
| "/usr/local/share/edge_driver" | |
| "/usr/local/share/emacs" | |
| "/usr/local/share/gecko_driver" | |
| "/usr/local/share/icons" | |
| "/usr/local/share/powershell" | |
| "/usr/local/share/vcpkg" | |
| "/usr/local/share/vim" | |
| "/usr/share/apache-maven-"* | |
| "/usr/share/gradle-"* | |
| "/usr/share/kotlinc" | |
| "/usr/share/miniconda" | |
| "/usr/share/php" | |
| "/usr/share/ri" | |
| "/usr/share/swift" | |
| # binaries | |
| "/usr/local/bin/azcopy" | |
| "/usr/local/bin/bicep" | |
| "/usr/local/bin/ccmake" | |
| "/usr/local/bin/cmake-"* | |
| "/usr/local/bin/cmake" | |
| "/usr/local/bin/cpack" | |
| "/usr/local/bin/ctest" | |
| "/usr/local/bin/helm" | |
| "/usr/local/bin/kind" | |
| "/usr/local/bin/kustomize" | |
| "/usr/local/bin/minikube" | |
| "/usr/local/bin/packer" | |
| "/usr/local/bin/phpunit" | |
| "/usr/local/bin/pulumi-"* | |
| "/usr/local/bin/pulumi" | |
| "/usr/local/bin/stack" | |
| # Haskell runtime | |
| "/usr/local/.ghcup" | |
| # Azure | |
| "/opt/az" | |
| "/usr/share/az_"* | |
| ) | |
| if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then | |
| # Environment variable set by GitHub Actions | |
| to_remove+=( | |
| "${AGENT_TOOLSDIRECTORY}" | |
| ) | |
| else | |
| echo "::warning::AGENT_TOOLSDIRECTORY is not set. Skipping removal." | |
| fi | |
| else | |
| # Remove folders and files present in AWS CodeBuild | |
| to_remove+=( | |
| # binaries | |
| "/usr/local/bin/ecs-cli" | |
| "/usr/local/bin/eksctl" | |
| "/usr/local/bin/kubectl" | |
| "${HOME}/.gradle" | |
| "${HOME}/.dotnet" | |
| "${HOME}/.goenv" | |
| "${HOME}/.phpenv" | |
| ) | |
| fi | |
| for element in "${to_remove[@]}"; do | |
| if [ ! -e "$element" ]; then | |
| # The file or directory doesn't exist. | |
| # Maybe it was removed in a newer version of the runner or it's not present in a | |
| # specific architecture (e.g. ARM). | |
| echo "::warning::Directory or file $element does not exist, skipping." | |
| fi | |
| done | |
| # Remove all files and directories at once to save time. | |
| sudo rm -rf "${to_remove[@]}" | |
| } | |
| execAndMeasureSpaceChange() { | |
| local operation=${1} # Function to execute | |
| local title=${2} | |
| local before | |
| before=$(getAvailableSpace) | |
| $operation | |
| printSavedSpace "$before" "$title" | |
| } | |
| # Remove large packages | |
| # REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh | |
| cleanPackages() { | |
| local packages=( | |
| '^aspnetcore-.*' | |
| '^dotnet-.*' | |
| '^llvm-.*' | |
| '^mongodb-.*' | |
| 'firefox' | |
| 'libgl1-mesa-dri' | |
| 'mono-devel' | |
| 'php.*' | |
| ) | |
| if isGitHubRunner; then | |
| packages+=( | |
| azure-cli | |
| ) | |
| if isX86; then | |
| packages+=( | |
| 'google-chrome-stable' | |
| 'google-cloud-cli' | |
| 'google-cloud-sdk' | |
| 'powershell' | |
| ) | |
| fi | |
| else | |
| packages+=( | |
| 'google-chrome-stable' | |
| ) | |
| fi | |
| sudo apt-get -qq remove -y --fix-missing "${packages[@]}" | |
| sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed" | |
| sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed" | |
| } | |
| # Remove Docker images. | |
| # Ubuntu 22 runners have docker images already installed. | |
| # They aren't present in ubuntu 24 runners. | |
| cleanDocker() { | |
| echo "=> Removing the following docker images:" | |
| sudo docker image ls | |
| echo "=> Removing docker images..." | |
| sudo docker image prune --all --force || true | |
| } | |
| # Remove Swap storage | |
| cleanSwap() { | |
| sudo swapoff -a || true | |
| sudo rm -rf /mnt/swapfile || true | |
| free -h | |
| } | |
| # Display initial disk space stats | |
| AVAILABLE_INITIAL=$(getAvailableSpace) | |
| printDF "BEFORE CLEAN-UP:" | |
| echo "" | |
| execAndMeasureSpaceChange cleanPackages "Unused packages" | |
| execAndMeasureSpaceChange cleanDocker "Docker images" | |
| execAndMeasureSpaceChange cleanSwap "Swap storage" | |
| execAndMeasureSpaceChange removeUnusedFilesAndDirs "Unused files and directories" | |
| # Output saved space statistic | |
| echo "" | |
| printDF "AFTER CLEAN-UP:" | |
| echo "" | |
| echo "" | |
| printSavedSpace "$AVAILABLE_INITIAL" "Total saved" |