#!/bin/bash # shellcheck disable=SC2086,SC2154,SC2155,SC2046,SC2001,SC2063 # Project [Source](https://github.com/rokibhasansagar/slimhub_actions.git) # License: [MIT](https://github.com/rokibhasansagar/slimhub_actions/blob/main/LICENSE) # Move to temporary directory cd "$(mktemp -d)" || exit 1 if [[ ${GITHUB_ACTIONS} != "true" || ${OSTYPE} != "linux-gnu" ]]; then printf "This Cleanup Script Is Intended For Ubuntu Runner.\n" exit 1 fi # Make Sure The Environment Is Non-Interactive export DEBIAN_FRONTEND=noninteractive # Stick A Warning Message For Breaking Changes function _warningMsg() { cat <<EOBT ╔══════════════════════════════════════════════════════════════╗ ║ ║ ║ The Project Script Changed Recently, ║ ║ There Are Many Breaking Changes. ║ ║ ------------------------------------------------ ║ ║ Please Read The README.md File Properly. ║ ║ ------------------------------------------------ ║ ║ Visit Here & Read Carefully: ║ ║ https://github.com/rokibhasansagar/slimhub_actions/#readme ║ ║ ║ ╚══════════════════════════════════════════════════════════════╝ EOBT } # Print the Warning Message printf "\e[33;1;2m" && _warningMsg && printf "\e[0m" && sleep 1s export AptPurgeList=" " DirPurgeList=" " # Make supported retainer list cat >/tmp/retainer.list <<EOR - homebrew - docker_imgcache - docker_buildkit + docker_imgcache - container_tools - android_sdk - java_tools + toolcache_java - database - browser_all + browser_firefox + browser_chrome + browser_edge - xvfb - webservers - php - cloud_cli - vcs - vim - dotnet - vcpkg - mono - ruby + toolcache_ruby - nodejs_npm + toolcache_node - pipx - toolcache_all + toolcache_codeql + toolcache_java + toolcache_pypy + toolcache_python + toolcache_ruby + toolcache_go + toolcache_node - compiler_all + compiler_gcc + compiler_gfortran + compiler_llvm_clang + compiler_cmake - powershell - rust - haskell - rlang - kotlin - julia - swift - snapd - manpages - libgtk EOR export retain=$(sed 's/\,/ /g;s/\s\s/ /g;s/-/_/g' <<<"${retain,,}") # Check if the values provided are correct or not for i in ${retain}; do if ! awk '{print $NF}' /tmp/retainer.list | sort -u | grep -q "^${i}$"; then echo -e "[!] Invalid Input: ${i}, Ignoring..." && continue fi export retain_${i}="true" && echo -e "[i] Retaining: ${i}" done echo "::group::<{[<]}> Raw Disk Space Before Cleanup <{[>]}>" df --sync -BM --output=pcent,used,avail / echo "::endgroup::" echo "::group:: {[+]} Temporary Apt Cache Update" sudo apt-fast update -qy echo "::endgroup::" if [[ ${retain_homebrew} != "true" ]]; then echo "::group:: {[-]} Clearing Homebrew" curl -sL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh -o uninstall-brew.sh chmod a+x uninstall-brew.sh NONINTERACTIVE=1 ./uninstall-brew.sh -f -q 2>/dev/null sudo rm -rf -- ./uninstall-brew.sh 2>/dev/null export DirPurgeList+=" /home/linuxbrew" echo "::endgroup::" fi if [[ ${retain_docker_buildkit} != "true" ]]; then export retain_docker_imgcache="false" fi if [[ ${retain_docker_imgcache} != "true" ]]; then echo "::group:: {[-]} Clearing Docker Image Caches" echo -e "The Following Docker Images Is Being Purged..." docker rmi -f $(docker images -q) 2>/dev/null echo "::endgroup::" fi if [[ ${retain_docker_buildkit} != "true" ]]; then export AptPurgeList+=" moby-buildx moby-cli moby-compose moby-containerd moby-engine moby-runc" export DirPurgeList+=" /usr/bin/docker-credential-ecr-login /usr/local/bin/docker-compose /usr/bin/docker*" fi if [[ ${retain_container_tools} != "true" ]]; then export AptPurgeList+=" podman buildah skopeo containers-common" export DirPurgeList+=" $(parallel -j4 echo /usr/local/bin/{} ::: kind kubectl helm minikube kustomize)" export DirPurgeList+=" /usr/local/bin/terraform" fi if [[ ${retain_android_sdk} != "true" ]]; then export DirPurgeList+=" $(parallel -j4 echo /usr/local/lib/android/sdk/{} ::: $(ls /usr/local/lib/android/sdk/))" export DirPurgeList+=" /usr/local/lib/android" fi if [[ ${retain_java_tools} != "true" ]]; then export AptPurgeList+=" temurin-*-jdk adoptopenjdk-* adoptium-ca-certificates openjdk-*" export retain_toolcache_java="false" export DirPurgeList+=" /usr/lib/jvm/ /usr/local/graalvm /usr/share/java/selenium-server*.jar" export DirPurgeList+=" /usr/share/*gradle* /usr/bin/gradle /usr/share/*maven* /usr/bin/mvn" export AptPurgeList+=" ant ant-optional" fi if [[ ${retain_database} != "true" ]]; then export AptPurgeList+=" postgresql-* libpq-dev libmysqlclient* msodbcsql* mssql-tools unixodbc-dev mysql-client* mysql-common mysql-server* php*-*sql sphinxsearch mongodb*" export DirPurgeList+=" /usr/share/mysql* /opt/mssql-tools /usr/local/sqlpackage" fi if [[ ${retain_browser_all} == "true" ]]; then for i in firefox chrome edge; do export retain_browser_${i}="true"; done fi if [[ ${retain_browser_firefox} != "true" ]]; then sudo bash -c 'cat >/etc/apt/preferences.d/mozilla-firefox' <<EOX Package: * Pin: release o=LP-PPA-mozillateam Pin-Priority: 1001 EOX export AptPurgeList+=" firefox" export DirPurgeList+=" /usr/lib/firefox /usr/local/share/gecko_driver /usr/bin/geckodriver" fi if [[ ${retain_browser_chrome} != "true" ]]; then export AptPurgeList+=" google-chrome-stable" export DirPurgeList+=" /usr/bin/google-chrome /usr/local/share/chrome_driver /usr/bin/chromedriver /usr/local/share/chromium /usr/bin/chromium /usr/bin/chromium-browser" fi if [[ ${retain_browser_edge} != "true" ]]; then export AptPurgeList+=" microsoft-edge-stable" export DirPurgeList+=" /usr/local/share/edge_driver /usr/bin/msedgedriver" fi if [[ ${retain_xvfb} != "true" ]]; then export AptPurgeList+=" xvfb" fi if [[ ${retain_webservers} != "true" ]]; then export AptPurgeList+=" apache2 apache2-* nginx nginx-*" fi if [[ ${retain_php} != "true" ]]; then export AptPurgeList+=" php-* php7* php8*" export DirPurgeList+=" /usr/share/php* /etc/php /usr/local/bin/phpunit" export DirPurgeList+=" /usr/bin/composer /home/runner/.config/composer /etc/skel/.composer /etc/.composer" fi if [[ ${retain_cloud_cli} != "true" ]]; then export AptPurgeList+=" session-manager-plugin azure-cli google-cloud-sdk" export DirPurgeList+=" /usr/local/bin/aliyun /usr/local/bin/aws /usr/local/bin/aws_completer /usr/local/aws-cli /usr/local/aws /usr/local/aws-sam-cli /usr/local/bin/azcopy* /usr/share/az_* /opt/az /usr/bin/az /usr/share/google-cloud-sdk /usr/local/bin/bicep /usr/local/bin/oc /usr/local/bin/oras" fi if [[ ${retain_vcs} != "true" ]]; then # git cli is kept back, hub and gh are removed export AptPurgeList+=" gh subversion mercurial" export DirPurgeList+=" /usr/local/bin/hub" fi if [[ ${retain_vim} != "true" ]]; then export AptPurgeList+=" vim vim-*" fi if [[ ${retain_dotnet} != "true" ]]; then export AptPurgeList+=" dotnet* aspnetcore*" export DirPurgeList+=" /usr/share/dotnet /home/runner/.dotnet /etc/skel/.dotnet/tools /etc/.dotnet/tools" fi if [[ ${retain_vcpkg} != "true" ]]; then export DirPurgeList+=" /usr/local/share/vcpkg /usr/local/bin/vcpkg" fi if [[ ${retain_mono} != "true" ]]; then export AptPurgeList+=" mono-* mono* libmono-* libmono* monodoc* msbuild nuget" fi if [[ ${retain_ruby} != "true" ]]; then export AptPurgeList+=" ruby* rake ri" export DirPurgeList+=" /usr/share/ri" export retain_toolcache_ruby="false" fi if [[ ${retain_nodejs_npm} != "true" ]]; then curl -sL "https://github.com/actions/runner-images/raw/main/images/linux/toolsets/toolset-$(lsb_release -rs | sed 's/\.//g').json" >/tmp/toolset.json sudo npm remove -g $(sed 's/^n$//g' <<<"$(jq -r ".node_modules[].name" /tmp/toolset.json)") &>/dev/null { yes | sudo n uninstall; } &>/dev/null export retain_toolcache_node="false" export DirPurgeList+=" /usr/local/n /usr/local/bin/n /usr/local/lib/node_modules /etc/skel/.nvm /home/runner/.nvm" fi if [[ "${retain_toolcache_pypy}" != "true" && "${retain_toolcache_python}" != "true" ]]; then export retain_pipx="false" fi if [[ ${retain_pipx} != "true" ]]; then { pipx uninstall-all && sudo pip3 uninstall -q -y pipx; } &>/dev/null export DirPurgeList+=" /opt/pipx /opt/pipx_bin" find /usr/share /usr/lib ~/.local/lib -depth -type d -name __pycache__ \ -exec rm -rf '{}' + &>/dev/null; fi if [[ ${retain_toolcache_all} == "true" ]]; then for i in CodeQL Java PyPy Python Ruby go node; do export retain_toolcache_${i,,}="true"; done fi if [[ "${retain_toolcache_codeql}" != "true" ]]; then export DirPurgeList+=" /opt/hostedtoolcache/CodeQL" fi if [[ "${retain_toolcache_java}" != "true" ]]; then export DirPurgeList+=" /opt/hostedtoolcache/Java*" fi if [[ "${retain_toolcache_pypy}" != "true" ]]; then export DirPurgeList+=" /opt/hostedtoolcache/PyPy" fi if [[ "${retain_toolcache_python}" != "true" ]]; then export DirPurgeList+=" /opt/hostedtoolcache/Python" fi if [[ "${retain_toolcache_ruby}" != "true" ]]; then export DirPurgeList+=" /opt/hostedtoolcache/Ruby" fi if [[ "${retain_toolcache_go}" != "true" ]]; then export DirPurgeList+=" /opt/hostedtoolcache/go" fi if [[ "${retain_toolcache_node}" != "true" ]]; then export DirPurgeList+=" /opt/hostedtoolcache/node" fi if [[ ${retain_compiler_all} == "true" ]]; then for i in gcc gfortran llvm_clang cmake; do export retain_compiler_${i}="true"; done fi if [[ ${retain_compiler_gcc} != "true" ]]; then case "$(lsb_release -rs)" in "22.04") export AptPurgeList+=" g++-9 g++-10 g++-12 gcc-9 gcc-10 gcc-12" ;; "20.04") export AptPurgeList+=" g++-10 g++-12 gcc-10 gcc-12" ;; esac fi if [[ ${retain_compiler_gfortran} != "true" ]]; then export AptPurgeList+=" gfortran-*" fi if [[ ${retain_compiler_llvm_clang} != "true" ]]; then export AptPurgeList+=" clang-* libclang* llvm-* libllvm* lldb-* lld-* clang-format-* clang-tidy-*" export DirPurgeList+=" /usr/lib/llvm-*" fi if [[ ${retain_compiler_cmake} != "true" ]]; then export DirPurgeList+=" $(parallel -j4 echo /usr/local/bin/{} ::: ccmake cmake cmake-gui cpack ctest)" export DirPurgeList+=" /usr/local/share/cmake-* /usr/local/*cmake* /usr/local/*/*cmake*" fi if [[ ${retain_powershell} != "true" ]]; then export AptPurgeList+=" powershell" export DirPurgeList+=" /opt/microsoft/powershell /usr/local/share/powershell" fi if [[ ${retain_rust} != "true" ]]; then export DirPurgeList+=" /usr/share/rust /home/runner/.cargo /home/runner/.rustup /etc/skel/.rustup /etc/skel/.cargo /etc/.rustup /etc/.cargo" fi if [[ ${retain_haskell} != "true" ]]; then ghcup nuke &>/dev/null || true export DirPurgeList+=" /usr/local/bin/stack /home/runner/.ghcup /usr/local/.ghcup" fi if [[ ${retain_rlang} != "true" ]]; then export AptPurgeList+=" r-base* r-cran* r-doc* r-recommended" fi if [[ ${retain_kotlin} != "true" ]]; then export DirPurgeList+=" /usr/share/kotlinc /usr/bin/kotlin*" fi if [[ ${retain_julia} != "true" ]]; then export DirPurgeList+=" /usr/local/julia* /usr/bin/julia" fi if [[ ${retain_swift} != "true" ]]; then export DirPurgeList+=" /usr/share/swift /usr/local/bin/swift /usr/local/bin/swiftc" fi if [[ ${retain_snapd} != "true" ]]; then { for i in lxd core20; do sudo snap remove --purge ${i}; done sudo snap remove --purge snapd } &>/dev/null sudo bash -c 'cat >/etc/apt/preferences.d/nosnap' <<EOX Package: snapd Pin: release a=* Pin-Priority: -10 EOX export AptPurgeList+=" snapd" export DirPurgeList+=" /var/cache/snapd /home/runner/snap" fi if [[ ${retain_manpages} != "true" ]]; then export AptPurgeList+=" man-db manpages" fi if [[ ${retain_libgtk} != "true" ]]; then export AptPurgeList+=" libgtk-3-* ubuntu-mono *-icon-theme" fi # TODO: Add Additional apt Packages to be Removed if [[ "$(lsb_release -rs)" == "20.04" ]]; then export AptPurgeList+=" esl-erlang" DirPurgeList+=" /usr/local/bin/rebar3" fi export AptPurgeList+=" imagemagick imagemagick-6-common libgl1-mesa-dri firebird* hhvm " export DirPurgeList+=" /usr/share/firebird* /opt/hhvm /usr/share/sbt /usr/bin/sbt /usr/local/share/phantomjs* /usr/local/bin/phantomjs /usr/local/bin/packer /usr/local/lib/lein /usr/local/bin/lein /usr/local/bin/pulumi /usr/local/bin/pulumi-* /usr/share/miniconda /usr/bin/conda /usr/local/lib/heroku" echo "::group:: {[-]} Uninstalling and Purging apt Packages" # Case #1. List has no missing packages / essential packages # All Done in Step 1 # Case #2. List has missing packages # Use _apt2unset to remove missing packages # and try again from start # Case #3. List has essential packages # Use _esscheck_process to deselect essentials # and try again from start # ESSCheck variables export ESStart='WARNING: The following essential packages will be removed.' ESEnd='0 upgraded, 0 newly installed' # main uninstaller function _apt2purge_base _apt2purge_base() { sudo -EH apt-get remove --quiet --assume-no --auto-remove --purge --fix-broken ${AptPurgeList} 1>/tmp/apt2purge.info 2>/tmp/apt2purge.log sed -i.bak 's/'$'\u001b''//g;s/\[1;31m//g;s/\[0m//g' /tmp/apt2purge.{info,log} 2>/dev/null } # function apt2unset _apt2unset() { echo -e "[i] Skipping Non-existing apt Packages: ${apt2unset}" for i in ${apt2unset}; do if grep -q '*' <<<"${i}"; then i=$(sed 's/\*/\\*/g' <<<"${i}"); fi export AptPurgeList=" ${AptPurgeList} " export AptPurgeList=$(sed 's/'" ${i} "'/ /g' <<<"${AptPurgeList}") done } # function _esscheck_process _esscheck_process() { if ! grep -q "${ESEnd}" /tmp/apt2purge.log; then export ESEnd=$(tail /tmp/apt2purge.log | grep -e "upgraded\|newly installed\|to remove") fi export EsnPackages=$(sed -e 's/'"${ESStart}"'/\n'"${ESStart}"'\n/' -e 's/'"${ESEnd}"'/\n'"${ESEnd}"'\n/' /tmp/apt2purge.log | sed -n '/'"${ESStart}"'/,/'"${ESEnd}"'/{//!p}' | sed -e '1d;2d;$d;s/due to //g;s/^\s\s//g;s/[()]//g' | tr ' ' '\n' | sort -u | paste -sd' ') export AllPackages=" $(grep '*' /tmp/apt2purge.log | sed 's/\*//g;s/^\s\s//g' | tr ' ' '\n' | sort -u | paste -sd' ') " export AllPackages=" ${AllPackages} " for i in ${EsnPackages}; do export AllPackages=$(sed 's/'" ${i} "'/ /g' <<<"${AllPackages}"); done } export -f _apt2purge_base _apt2unset _esscheck_process # The whole function serves as Case #3 _apt2purge_on_essfix() { # The following function handles Case #1 _apt2purge_base export apt2unset=$(grep "Unable to locate package" /tmp/apt2purge.log | awk '{print $NF}' | paste -sd' ') # The following conditional block handles Case #2 if [[ ${apt2unset} != "" ]]; then if grep -q "hhvm" <<<"${apt2unset}"; then export DirPurgeList=$(sed 's|\s/opt/hhvm||g' <<<"${DirPurgeList}"); fi _apt2unset if [[ ${AptPurgeList} != "" && ${AptPurgeList} != " " ]]; then _apt2purge_base fi fi } echo -e "[i] List of apt Packages to be Removed: ${AptPurgeList}" # >>> Case #1 + Case #2 _apt2purge_on_essfix # >>> Case #3: Has Essential packages if grep -q "${ESStart}" /tmp/apt2purge.log; then _esscheck_process export AptPurgeList=" ${AllPackages} " _apt2purge_on_essfix fi # Redendent apt cleanup sudo apt autoclean >/dev/null || true sudo apt autoremove -qy 2>/dev/null || true sudo rm -rf /var/cache/apt/archives/ 2>/dev/null || true echo "::endgroup::" echo "::group:: {[-]} Purging Unnecessary Files and Directories" parallel 'echo -e "Purging {}" && sudo rm -rf -- {}' ::: ${DirPurgeList} # Delete broken symlinks sudo find /home/runner/.local/ /home/runner/ /usr/share/ /usr/bin/ /usr/local/bin/ /usr/local/share/ /usr/local/ /opt/ /snap/ -xtype l -delete 2>/dev/null echo "::endgroup::" echo "::group:: {[-]} Clearing Journal Logs" sudo journalctl --rotate && sudo journalctl --vacuum-time=1s sudo find /var/log -type f -regex ".*\.gz$" -delete sudo find /var/log -type f -regex ".*\.[0-9]$" -delete sudo find /var/log/ -type f -exec sudo cp /dev/null {} \; echo "::endgroup::" echo "::group::<{[>]}> Free Disk Space After Cleanup <{[<]}>" df --sync -BM --output=pcent,used,avail / echo "::endgroup::"