11#! /bin/bash
2+ set  -euo pipefail
23
3- #  Free disk space on Linux GitHub action runners
4+ #  Free disk space on Linux GitHub action runners. 
45#  Script inspired by https://github.com/jlumbroso/free-disk-space
56
6- #  print a line of the specified character
7+ #  When updating to a new ubuntu version (e.g. from ubuntu-24.04):
8+ #  - Check that there are no docker images preinstalled with `docker image ls`
9+ #  - Check that there are no big packages preinstalled that we aren't using
10+ #  - Check that all directores we are removing are still present (look at the warnings)
11+ 
12+ #  Print a line of the specified character.
713printSeparationLine () {
814    for  (( i =  0 ; i <  80 ; i++ )) ;  do 
915        printf  " %s"   " $1 " 
1016    done 
1117    printf  " \n" 
1218}
1319
14- #  compute  available space
20+ #  Compute  available space. 
1521#  REF: https://unix.stackexchange.com/a/42049/60849
1622#  REF: https://stackoverflow.com/a/450821/408734
17- getAvailableSpace () { echo  $( df -a |  awk ' NR > 1 {avail+=$4} END {print avail}' ) ;  }
23+ getAvailableSpace () {
24+     df -a |  awk ' NR > 1 {avail+=$4} END {print avail}' 
25+ }
1826
19- #  make  Kb human readable (assume the input is Kb)
27+ #  Make  Kb human readable (assume the input is Kb). 
2028#  REF: https://unix.stackexchange.com/a/44087/60849
21- formatByteCount () { echo  $( numfmt --to=iec-i --suffix=B --padding=7 $1 ' 000' ) ;  }
29+ formatByteCount () {
30+     numfmt --to=iec-i --suffix=B --padding=7 " $1 " ' 000' 
31+ }
32+ 
33+ #  Check if the architecture is x86.
34+ isX86 () {
35+     local  arch
36+     arch=$( uname -m) 
37+     if  [ " $arch "   =  " x86_64"   ];  then 
38+         return  0
39+     else 
40+         return  1
41+     fi 
42+ }
43+ 
44+ #  Execute a task, printing how much space was saved and how long it took to run the task
45+ execAndMeasure () {
46+     local  task_name=${1} 
47+ 
48+     local  start
49+     start=$( date +%s) 
50+ 
51+     local  before
52+     before=$( getAvailableSpace) 
53+ 
54+     #  Run the task. Skip the first argument because it's the task name.
55+     " ${@: 2} " 
2256
23- #  macro to output saved space
24- printSavedSpace () {
25-     #  Disk space before the operation
26-     local  before=${1} 
27-     local  title=${2:- } 
57+     local  end
58+     end=$( date +%s) 
2859
2960    local  after
3061    after=$( getAvailableSpace) 
62+ 
63+     #  How much space was saved.
3164    local  saved=$(( after -  before)) 
65+     #  How long the task took.
66+     local  seconds_taken=$(( end -  start)) 
3267
33-     echo  " " 
34-     printSeparationLine " *" 
35-     if  [ -n  " ${title} "   ];  then 
36-         echo  " => ${title} : Saved $( formatByteCount " $saved " ) " 
37-     else 
38-         echo  " => Saved $( formatByteCount " $saved " ) " 
39-     fi 
40-     printSeparationLine " *" 
41-     echo  " " 
68+     echo  " ==> ${task_name} : Saved $( formatByteCount " $saved " )   in $seconds_taken  seconds" 
4269}
4370
44- #  macro to print  output of df with caption
71+ #  Print  output of df with caption. It shows information about disk space. 
4572printDF () {
4673    local  caption=${1} 
4774
@@ -52,91 +79,164 @@ printDF() {
5279    echo  " " 
5380    df -h
5481    printSeparationLine " =" 
82+     echo  " " 
5583}
5684
57- removeDir () {
58-     dir=${1} 
59- 
60-     local  before
61-     before=$( getAvailableSpace) 
62- 
63-     sudo rm -rf " $dir "   ||  true 
85+ removeUnusedDirsAndFiles () {
86+     local  to_remove=(
87+         " /etc/mysql" 
88+         " /usr/local/aws-sam-cli" 
89+         " /usr/local/doc/cmake" 
90+         " /usr/local/julia" * 
91+         " /usr/local/lib/android" 
92+         " /usr/local/share/chromedriver-" * 
93+         " /usr/local/share/chromium" 
94+         " /usr/local/share/cmake-" * 
95+         " /usr/local/share/edge_driver" 
96+         " /usr/local/share/gecko_driver" 
97+         " /usr/local/share/icons" 
98+         " /usr/local/share/vim" 
99+         " /usr/local/share/emacs" 
100+         " /usr/local/share/powershell" 
101+         " /usr/local/share/vcpkg" 
102+         " /usr/share/apache-maven-" * 
103+         " /usr/share/gradle-" * 
104+         " /usr/share/java" 
105+         " /usr/share/kotlinc" 
106+         " /usr/share/miniconda" 
107+         " /usr/share/php" 
108+         " /usr/share/ri" 
109+         " /usr/share/swift" 
110+ 
111+         #  binaries
112+         " /usr/local/bin/azcopy" 
113+         " /usr/local/bin/bicep" 
114+         " /usr/local/bin/ccmake" 
115+         " /usr/local/bin/cmake-" * 
116+         " /usr/local/bin/cmake" 
117+         " /usr/local/bin/cpack" 
118+         " /usr/local/bin/ctest" 
119+         " /usr/local/bin/helm" 
120+         " /usr/local/bin/kind" 
121+         " /usr/local/bin/kustomize" 
122+         " /usr/local/bin/minikube" 
123+         " /usr/local/bin/packer" 
124+         " /usr/local/bin/phpunit" 
125+         " /usr/local/bin/pulumi-" * 
126+         " /usr/local/bin/pulumi" 
127+         " /usr/local/bin/stack" 
128+ 
129+         #  Haskell runtime
130+         " /usr/local/.ghcup" 
131+ 
132+         #  Azure
133+         " /opt/az" 
134+         " /usr/share/az_" * 
135+ 
136+         #  Environemnt variable set by GitHub Actions
137+         " $AGENT_TOOLSDIRECTORY " 
138+     )
139+ 
140+     local  existing=()
141+     for  element  in  " ${to_remove[@]} " ;  do 
142+         if  [ !  -e  " $element "   ];  then 
143+             echo  " ::warning::Directory or file $element  does not exist, skipping." 
144+         else 
145+             existing+=(" $element "  )
146+         fi 
147+     done 
64148
65-     printSavedSpace  " $before "   " $dir " 
149+     execAndMeasure  " Removed unused directories "  sudo rm -rf  " ${existing[@]} " 
66150}
67151
68- execAndMeasureSpaceChange () {
69-     local  operation=${1}  #  Function to execute
70-     local  title=${2} 
71- 
72-     local  before
73-     before=$( getAvailableSpace) 
74-     $operation 
75- 
76-     printSavedSpace " $before "   " $title " 
152+ removeNodeModules () {
153+     sudo npm uninstall -g \
154+         " @bazel/bazelisk"   \
155+         " grunt"             \
156+         " gulp"              \
157+         " lerna"             \
158+         " n"                 \
159+         " newman"            \
160+         " parcel"            \
161+         " typescript"        \
162+         " webpack-cli"       \
163+         " webpack"           \
164+         " yarn" 
77165}
78166
79- #  Remove large packages
80- #  REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh
167+ #  Remove unused packages.
81168cleanPackages () {
82-     sudo apt-get -qq remove -y --fix-missing \
83-         ' ^aspnetcore-.*'         \
84-         ' ^dotnet-.*'             \
85-         ' ^llvm-.*'               \
86-         ' php.*'                  \
87-         ' ^mongodb-.*'            \
88-         ' ^mysql-.*'              \
89-         ' azure-cli'              \
90-         ' google-chrome-stable'   \
91-         ' firefox'                \
92-         ' powershell'             \
93-         ' mono-devel'             \
94-         ' libgl1-mesa-dri'        \
95-         ' google-cloud-sdk'       \
96-         ' google-cloud-cli' 
169+     local  packages=(
170+         ' .*-icon-theme$' 
171+         ' ^aspnetcore-.*' 
172+         ' ^dotnet-.*' 
173+         ' ^java-*' 
174+         ' ^libllvm.*' 
175+         ' ^llvm-.*' 
176+         ' ^mercurial.*' 
177+         ' ^mysql-.*' 
178+         ' ^vim.*' 
179+         ' ^fonts-.*' 
180+         ' azure-cli' 
181+         ' buildah' 
182+         ' cpp-13' 
183+         ' firefox' 
184+         ' gcc-12' 
185+         ' gcc-13' 
186+         ' gcc-14' 
187+         ' gcc' 
188+         ' g++-14' 
189+         ' gfortran-14' 
190+         ' groff-base' 
191+         ' kubectl' 
192+         ' libgl1-mesa-dri' 
193+         ' microsoft-edge-stable' 
194+         ' php.*' 
195+         ' podman' 
196+         ' powershell' 
197+         ' skopeo' 
198+         ' snapd' 
199+         ' tmux' 
200+     )
201+ 
202+     if  isX86;  then 
203+         packages+=(
204+             ' google-chrome-stable' 
205+             ' google-cloud-cli' 
206+         )
207+     fi 
208+ 
209+     sudo apt-get purge -y --autoremove --fix-missing " ${packages[@]} " 
97210
211+     echo  " => apt-get autoremove" 
98212    sudo apt-get autoremove -y ||  echo  " ::warning::The command [sudo apt-get autoremove -y] failed" 
99213    sudo apt-get clean ||  echo  " ::warning::The command [sudo apt-get clean] failed failed" 
100214}
101215
102- #  Remove Docker images
103- cleanDocker () {
104-     echo  " Removing the following docker images:" 
105-     sudo docker image ls
106-     echo  " Removing docker images..." 
107-     sudo docker image prune --all --force ||  true 
108- }
109- 
110- #  Remove Swap storage
111- cleanSwap () {
112-     sudo swapoff -a ||  true 
113-     sudo rm -rf /mnt/swapfile ||  true 
114-     free -h
115- }
116- 
117- #  Display initial disk space stats
118- 
119- AVAILABLE_INITIAL=$( getAvailableSpace) 
216+ removePythonPackages () {
217+     local  packages=(
218+     )
120219
121- printDF  " BEFORE CLEAN-UP: " 
122- echo   " " 
123- 
124- removeDir /usr/local/lib/android 
125- removeDir /usr/share/dotnet 
220+      if  isX86 ;   then 
221+         packages+=( 
222+              ' ansible-core ' 
223+         ) 
224+      fi 
126225
127- #  Haskell runtime
128- removeDir /opt/ghc
129- removeDir /usr/local/.ghcup
226+     for  p  in  " ${packages[@]} " ;  do 
227+         sudo pipx uninstall " $p " 
228+     done 
229+ }
130230
131- execAndMeasureSpaceChange cleanPackages " Large misc. packages" 
132- execAndMeasureSpaceChange cleanDocker " Docker images" 
133- execAndMeasureSpaceChange cleanSwap " Swap storage" 
231+ main () {
232+     printDF " BEFORE CLEAN-UP:" 
134233
135- #  Output saved space statistic
136- echo  " " 
137- printDF " AFTER CLEAN-UP:" 
234+     execAndMeasure " Unused packages"   cleanPackages
235+     execAndMeasure " Node modules"   removeNodeModules
236+     execAndMeasure " Python Packages"   removePythonPackages
237+     removeUnusedDirsAndFiles
138238
139- echo   " " 
140- echo   " " 
239+     printDF  " AFTER CLEAN-UP: " 
240+ } 
141241
142- printSavedSpace  " $AVAILABLE_INITIAL "   " Total saved " 
242+ execAndMeasure  " Total"  main 
0 commit comments