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} 
2247
23- #  macro to output saved space
24- printSavedSpace () {
25-     #  Disk space before the operation
26-     local  before=${1} 
27-     local  title=${2:- } 
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} " 
56+ 
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 " ) $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,171 @@ 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 
64- 
65-     printSavedSpace " $before " " $dir " 
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+     for  element  in  " ${to_remove[@]} " ;  do 
141+         if  [ !  -e  " $element " ;  then 
142+             echo  " ::warning::Directory or file $element  does not exist, skipping." 
143+         else 
144+             execAndMeasure " Removed $element " " $element " 
145+         fi 
146+     done 
66147}
67148
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 " 
149+ removeNodeModules () {
150+     sudo npm uninstall -g \
151+         " @bazel/bazelisk" 
152+         " bazel" 
153+         " grunt" 
154+         " gulp" 
155+         " lerna" 
156+         " n" 
157+         " newman" 
158+         " parcel" 
159+         " typescript" 
160+         " webpack-cli" 
161+         " webpack" 
162+         " yarn" 
77163}
78164
79- #  Remove large packages
80- #  REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh
165+ #  Remove unused packages.
81166cleanPackages () {
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' 
167+     local  packages=(
168+         ' .*-icon-theme$' 
169+         ' ^aspnetcore-.*' 
170+         ' ^dotnet-.*' 
171+         ' ^java-*' 
172+         ' ^libllvm.*' 
173+         ' ^llvm-.*' 
174+         ' ^mercurial.*' 
175+         ' ^mysql-.*' 
176+         ' ^vim.*' 
177+         ' ^fonts-.*' 
178+         ' azure-cli' 
179+         ' buildah' 
180+         ' cpp-13' 
181+         ' firefox' 
182+         ' gcc-12' 
183+         ' gcc-13' 
184+         ' gcc-14' 
185+         ' gcc' 
186+         ' g++-14' 
187+         ' gfortran-14' 
188+         ' groff-base' 
189+         ' kubectl' 
190+         ' libgl1-mesa-dri' 
191+         ' microsoft-edge-stable' 
192+         ' php.*' 
193+         ' podman' 
194+         ' powershell' 
195+         ' skopeo' 
196+         ' snapd' 
197+         ' tmux' 
198+     )
199+ 
200+     if  isX86;  then 
201+         packages+=(
202+             ' google-chrome-stable' 
203+             ' google-cloud-cli' 
204+         )
205+     fi 
206+ 
207+     sudo apt-get purge -y --autoremove --fix-missing " ${packages[@]} " 
97208
209+     echo  " => apt-get autoremove" 
98210    sudo apt-get autoremove -y ||  echo  " ::warning::The command [sudo apt-get autoremove -y] failed" 
99211    sudo apt-get clean ||  echo  " ::warning::The command [sudo apt-get clean] failed failed" 
100212}
101213
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- 
110214#  Remove Swap storage
111215cleanSwap () {
112216    sudo swapoff -a ||  true 
113217    sudo rm -rf /mnt/swapfile ||  true 
114218    free -h
115219}
116220
117- #  Display initial disk space stats 
118- 
119- AVAILABLE_INITIAL= $( getAvailableSpace ) 
221+ removePythonPackages () { 
222+      local  packages=( 
223+      )
120224
121- printDF " BEFORE CLEAN-UP:" 
122- echo  " " 
225+     if  isX86;  then 
226+         packages+=(
227+             ' ansible-core' 
228+         )
229+     fi 
123230
124- removeDir /usr/local/lib/android
125- removeDir /usr/share/dotnet
231+     for  p  in  " ${packages[@]} " ;  do 
232+         sudo pipx uninstall " $p " 
233+     done 
234+ }
126235
127- #  Haskell runtime
128- removeDir /opt/ghc
129- removeDir /usr/local/.ghcup
236+ main () {
237+     printDF " BEFORE CLEAN-UP:" 
130238
131- execAndMeasureSpaceChange cleanPackages " Large misc. packages" 
132- execAndMeasureSpaceChange cleanDocker " Docker images" 
133- execAndMeasureSpaceChange cleanSwap " Swap storage" 
239+     execAndMeasure " Unused packages" 
240+     execAndMeasure " Swap storage" 
241+     execAndMeasure " Node modules" 
242+     execAndMeasure " Python Packages" 
134243
135- #  Output saved space statistic
136- echo  " " 
137- printDF " AFTER CLEAN-UP:" 
244+     removeUnusedDirsAndFiles
138245
139- echo   " " 
140- echo   " " 
246+     printDF  " AFTER CLEAN-UP: " 
247+ } 
141248
142- printSavedSpace  " $AVAILABLE_INITIAL "   " Total saved " 
249+ execAndMeasure  " Total"  main 
0 commit comments