Clarify.
[tails-test.git] / Rakefile
blob332c08a338a7482787121970a305eb346f4059b1
1 # -*- coding: utf-8 -*-
2 # -*- mode: ruby -*-
3 # vi: set ft=ruby :
5 # Tails: The Amnesic Incognito Live System
6 # Copyright © 2012 Tails developers <tails@boum.org>
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 require 'rbconfig'
22 require 'rubygems'
23 require 'vagrant'
24 require 'uri'
26 $:.unshift File.expand_path('../vagrant/lib', __FILE__)
27 require 'tails_build_settings'
28 require 'vagrant_version'
30 # Path to the directory which holds our Vagrantfile
31 VAGRANT_PATH = File.expand_path('../vagrant', __FILE__)
33 # Branches that are considered 'stable' (used to select SquashFS compression)
34 STABLE_BRANCH_NAMES = ['stable', 'testing']
36 # Environment variables that will be exported to the build script
37 EXPORTED_VARIABLES = ['http_proxy', 'MKSQUASHFS_OPTIONS', 'TAILS_RAM_BUILD', 'TAILS_CLEAN_BUILD', 'TAILS_BOOTSTRAP_CACHE']
39 # Let's save the http_proxy set before playing with it
40 EXTERNAL_HTTP_PROXY = ENV['http_proxy']
42 # In-VM proxy URL
43 INTERNEL_HTTP_PROXY = "http://#{VIRTUAL_MACHINE_HOSTNAME}:3142"
45 def primary_vm
46   env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
47   if vagrant_old
48     return env.primary_vm
49   else
50     name = env.primary_machine_name
51     return env.machine(name, env.default_provider)
52   end
53 end
55 def primary_vm_state
56   if vagrant_old
57     return primary_vm.state
58   else
59     return primary_vm.state.id
60   end
61 end
63 def primary_vm_chan
64   if vagrant_old
65     return primary_vm.channel
66   else
67     return primary_vm.communicate
68   end
69 end
72 def vm_id
73   if vagrant_old
74     primary_vm.uuid
75   else
76     primary_vm.id
77   end
78 end
80 def vm_driver
81   if vagrant_old
82     primary_vm.driver
83   else
84     primary_vm.provider.driver
85   end
86 end
88 def current_vm_cpus
89   info = vm_driver.execute 'showvminfo', vm_id, '--machinereadable'
90   $1.to_i if info =~ /^cpus=(\d+)/
91 end
93 def vm_running?
94   primary_vm_state == :running
95 end
97 def enough_free_host_memory_for_ram_build?
98   return false unless RbConfig::CONFIG['host_os'] =~ /linux/i
100   begin
101     usable_free_mem = `free`.split[16].to_i
102     usable_free_mem > VM_MEMORY_FOR_RAM_BUILDS * 1024
103   rescue
104     false
105   end
108 def free_vm_memory
109   primary_vm_chan.execute("free", :error_check => false) do |fd, data|
110     return data.split[16].to_i
111   end
114 def enough_free_vm_memory_for_ram_build?
115   free_vm_memory > BUILD_SPACE_REQUIREMENT * 1024
118 def enough_free_memory_for_ram_build?
119   if vm_running?
120     enough_free_vm_memory_for_ram_build?
121   else
122     enough_free_host_memory_for_ram_build?
123   end
126 def is_release?
127   branch_name = `git name-rev --name-only HEAD`
128   tag_name = `git describe --exact-match HEAD 2> /dev/null`
129   STABLE_BRANCH_NAMES.include? branch_name.chomp or tag_name.chomp.length > 0
132 def system_cpus
133   return nil unless RbConfig::CONFIG['host_os'] =~ /linux/i
135   begin
136     File.read('/proc/cpuinfo').scan(/^processor\s+:/).count
137   rescue
138     nil
139   end
142 task :parse_build_options do
143   options = ''
145   # Default to in-memory builds if there is enough RAM available
146   options += 'ram ' if enough_free_memory_for_ram_build?
148   # Use in-VM proxy unless an external proxy is set
149   options += 'vmproxy ' unless EXTERNAL_HTTP_PROXY
151   # Default to fast compression on development branches
152   options += 'gzipcomp ' unless is_release?
154   # Make sure release builds are clean
155   options += 'cleanall ' if is_release?
157   # Default to the number of system CPUs when we can figure it out
158   cpus = system_cpus
159   options += "cpus=#{cpus} " if cpus
161   options += ENV['TAILS_BUILD_OPTIONS'] if ENV['TAILS_BUILD_OPTIONS']
162   options.split(' ').each do |opt|
163     case opt
164     # Memory build settings
165     when 'ram'
166       ENV['TAILS_RAM_BUILD'] = '1'
167     when 'noram'
168       ENV['TAILS_RAM_BUILD'] = nil
169     # Bootstrap cache settings
170     when 'cache'
171       ENV['TAILS_BOOTSTRAP_CACHE'] = '1'
172     when 'nocache'
173       ENV['TAILS_BOOTSTRAP_CACHE'] = nil
174     # HTTP proxy settings
175     when 'extproxy'
176       abort "No HTTP proxy set, but one is required by TAILS_BUILD_OPTIONS. Aborting." unless EXTERNAL_HTTP_PROXY
177       ENV['http_proxy'] = EXTERNAL_HTTP_PROXY
178     when 'vmproxy'
179       ENV['http_proxy'] = INTERNEL_HTTP_PROXY
180     when 'noproxy'
181       ENV['http_proxy'] = nil
182     # SquashFS compression settings
183     when 'gzipcomp'
184       ENV['MKSQUASHFS_OPTIONS'] = '-comp gzip'
185     when 'defaultcomp'
186       ENV['MKSQUASHFS_OPTIONS'] = nil
187     # Clean-up settings
188     when 'cleanall'
189       ENV['TAILS_CLEAN_BUILD'] = '1'
190     # Virtual CPUs settings
191     when /cpus=(\d+)/
192       ENV['TAILS_BUILD_CPUS'] = $1
193     # Git settings
194     when 'ignorechanges'
195       ENV['TAILS_BUILD_IGNORE_CHANGES'] = '1'
196     end
197   end
200 task :ensure_clean_repository do
201   unless `git status --porcelain`.empty?
202     if ENV['TAILS_BUILD_IGNORE_CHANGES']
203       $stderr.puts <<-END_OF_MESSAGE.gsub(/^        /, '')
205         You have uncommited changes in the Git repository. They will
206         be ignored for the upcoming build.
208       END_OF_MESSAGE
209     else
210       $stderr.puts <<-END_OF_MESSAGE.gsub(/^        /, '')
212         You have uncommited changes in the Git repository. Due to limitations
213         of the build system, you need to commit them before building Tails.
215         If you don't care about those changes and want to build Tails nonetheless,
216         please add `ignorechanges` to the TAILS_BUILD_OPTIONS environment
217         variable.
219       END_OF_MESSAGE
220       abort 'Uncommited changes. Aborting.'
221     end
222   end
225 task :validate_http_proxy do
226   if ENV['http_proxy']
227     proxy_host = URI.parse(ENV['http_proxy']).host
229     if proxy_host.nil?
230       ENV['http_proxy'] = nil
231       $stderr.puts "Ignoring invalid HTTP proxy."
232       return
233     end
235     if ['localhost', '[::1]'].include?(proxy_host) || proxy_host.start_with?('127.0.0.')
236       abort 'Using an HTTP proxy listening on the loopback is doomed to fail. Aborting.'
237     end
239     $stderr.puts "Using HTTP proxy: #{ENV['http_proxy']}"
240   else
241     $stderr.puts "No HTTP proxy set."
242   end
245 desc 'Build Tails'
246 task :build => ['parse_build_options', 'ensure_clean_repository', 'validate_http_proxy', 'vm:up'] do
248   if ENV['TAILS_RAM_BUILD'] && not(enough_free_memory_for_ram_build?)
249     $stderr.puts <<-END_OF_MESSAGE.gsub(/^      /, '')
251       The virtual machine is not currently set with enough memory to
252       perform an in-memory build. Either remove the `ram` option from
253       the TAILS_BUILD_OPTIONS environment variable, or shut the
254       virtual machine down using `rake vm:halt` before trying again.
256     END_OF_MESSAGE
257     abort 'Not enough memory for the virtual machine to run an in-memory build. Aborting.'
258   end
260   if ENV['TAILS_BUILD_CPUS'] && current_vm_cpus != ENV['TAILS_BUILD_CPUS'].to_i
261     $stderr.puts <<-END_OF_MESSAGE.gsub(/^      /, '')
263       The virtual machine is currently running with #{current_vm_cpus}
264       virtual CPU(s). In order to change that number, you need to
265       stop the VM first, using `rake vm:halt`. Otherwise, please
266       adjust the `cpus` options accordingly.
268     END_OF_MESSAGE
269     abort 'The virtual machine needs to be reloaded to change the number of CPUs. Aborting.'
270   end
272   exported_env = EXPORTED_VARIABLES.select { |k| ENV[k] }.
273                   collect { |k| "#{k}='#{ENV[k]}'" }.join(' ')
274   status = primary_vm_chan.execute("#{exported_env} build-tails",
275                                    :error_check => false) do |fd, data|
276     (fd == :stdout ? $stdout : $stderr).write data
277   end
279   # Move build products to the current directory
280   FileUtils.mv Dir.glob("#{VAGRANT_PATH}/tails-*"),
281                File.expand_path('..', __FILE__), :force => true
283   exit status
286 namespace :vm do
287   desc 'Start the build virtual machine'
288   task :up => ['parse_build_options', 'validate_http_proxy'] do
289     case primary_vm_state
290     when :not_created
291       # Do not use non-existant in-VM proxy to download the basebox
292       if ENV['http_proxy'] == INTERNEL_HTTP_PROXY
293         ENV['http_proxy'] = nil
294         restore_internal_proxy = true
295       end
297       $stderr.puts <<-END_OF_MESSAGE.gsub(/^        /, '')
299         This is the first time that the Tails builder virtual machine is
300         started. The virtual machine template is about 300 MB to download,
301         so the process might take some time.
303         Please remember to shut the virtual machine down once your work on
304         Tails is done:
306             $ rake vm:halt
308       END_OF_MESSAGE
309     when :poweroff
310       $stderr.puts <<-END_OF_MESSAGE.gsub(/^        /, '')
312         Starting Tails builder virtual machine. This might take a short while.
313         Please remember to shut it down once your work on Tails is done:
315             $ rake vm:halt
317       END_OF_MESSAGE
318     end
319     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
320     result = env.cli('up')
321     abort "'vagrant up' failed" unless result
323     ENV['http_proxy'] = INTERNEL_HTTP_PROXY if restore_internal_proxy
324   end
326   desc 'Stop the build virtual machine'
327   task :halt do
328     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
329     result = env.cli('halt')
330     abort "'vagrant halt' failed" unless result
331   end
333   desc 'Re-run virtual machine setup'
334   task :provision => ['parse_build_options', 'validate_http_proxy'] do
335     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
336     result = env.cli('provision')
337     abort "'vagrant provision' failed" unless result
338   end
340   desc 'Destroy build virtual machine (clean up all files)'
341   task :destroy do
342     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
343     result = env.cli('destroy', '--force')
344     abort "'vagrant destroy' failed" unless result
345   end