Close pending bugs and todo
[tails-test.git] / Rakefile
blob9a4f4b6af3ae91e1f1b4ad8313663bb7615bf37d
1 # -*- mode: ruby -*-
2 # vi: set ft=ruby :
4 # Tails: The Amnesic Incognito Live System
5 # Copyright © 2012 Tails developers <tails@boum.org>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 require 'rbconfig'
21 require 'rubygems'
22 require 'vagrant'
23 require 'uri'
25 $:.unshift File.expand_path('../vagrant/lib', __FILE__)
26 require 'tails_build_settings'
28 # Path to the directory which holds our Vagrantfile
29 VAGRANT_PATH = File.expand_path('../vagrant', __FILE__)
31 # Branches that are considered 'stable' (used to select SquashFS compression)
32 STABLE_BRANCH_NAMES = ['stable', 'testing']
34 # Environment variables that will be exported to the build script
35 EXPORTED_VARIABLES = ['http_proxy', 'MKSQUASHFS_OPTIONS', 'TAILS_RAM_BUILD', 'TAILS_CLEAN_BUILD']
37 # Let's save the http_proxy set before playing with it
38 EXTERNAL_HTTP_PROXY = ENV['http_proxy']
40 # In-VM proxy URL
41 INTERNEL_HTTP_PROXY = "http://#{VIRTUAL_MACHINE_HOSTNAME}:3142"
43 def current_vm_memory
44   env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
45   uuid = env.primary_vm.uuid
46   info = env.primary_vm.driver.execute 'showvminfo', uuid, '--machinereadable'
47   $1.to_i if info =~ /^memory=(\d+)/
48 end
50 def current_vm_cpus
51   env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
52   uuid = env.primary_vm.uuid
53   info = env.primary_vm.driver.execute 'showvminfo', uuid, '--machinereadable'
54   $1.to_i if info =~ /^cpus=(\d+)/
55 end
57 def enough_free_memory?
58   return false unless RbConfig::CONFIG['host_os'] =~ /linux/i
60   begin
61     usable_free_mem = `free`.split[16].to_i
62     usable_free_mem > VM_MEMORY_FOR_RAM_BUILDS * 1024
63   rescue
64     false
65   end
66 end
68 def stable_branch?
69   branch_name = `git name-rev --name-only HEAD`
70   STABLE_BRANCH_NAMES.include? branch_name
71 end
73 def system_cpus
74   return nil unless RbConfig::CONFIG['host_os'] =~ /linux/i
76   begin
77     File.read('/proc/cpuinfo').scan(/^processor\s+:/).count
78   rescue
79     nil
80   end
81 end
83 task :parse_build_options do
84   options = ''
86   # Default to in-memory builds if there is enough RAM available
87   options += 'ram ' if enough_free_memory?
89   # Use in-VM proxy unless an external proxy is set
90   options += 'vmproxy ' unless EXTERNAL_HTTP_PROXY
92   # Default to fast compression on development branches
93   options += 'gzipcomp ' unless stable_branch?
95   # Default to the number of system CPUs when we can figure it out
96   cpus = system_cpus
97   options += "cpus=#{cpus} " if cpus
99   options += ENV['TAILS_BUILD_OPTIONS'] if ENV['TAILS_BUILD_OPTIONS']
100   options.split(' ').each do |opt|
101     case opt
102     # Memory build settings
103     when 'ram'
104       abort "Not enough free memory to do an in-memory build. Aborting." unless enough_free_memory?
105       ENV['TAILS_RAM_BUILD'] = '1'
106     when 'noram'
107       ENV['TAILS_RAM_BUILD'] = nil
108     # HTTP proxy settings
109     when 'extproxy'
110       abort "No HTTP proxy set, but one is required by TAILS_BUILD_OPTIONS. Aborting." unless EXTERNAL_HTTP_PROXY
111       ENV['http_proxy'] = EXTERNAL_HTTP_PROXY
112     when 'vmproxy'
113       ENV['http_proxy'] = INTERNEL_HTTP_PROXY
114     when 'noproxy'
115       ENV['http_proxy'] = nil
116     # SquashFS compression settings
117     when 'gzipcomp'
118       ENV['MKSQUASHFS_OPTIONS'] = '-comp gzip'
119     when 'defaultcomp'
120       ENV['MKSQUASHFS_OPTIONS'] = nil
121     # Clean-up settings
122     when 'cleanall'
123       ENV['TAILS_CLEAN_BUILD'] = '1'
124     # Virtual CPUs settings
125     when /cpus=(\d+)/
126       ENV['TAILS_BUILD_CPUS'] = $1
127     # Git settings
128     when 'ignorechanges'
129       ENV['TAILS_BUILD_IGNORE_CHANGES'] = '1'
130     end
131   end
134 task :ensure_clean_repository do
135   unless `git status --porcelain`.empty?
136     if ENV['TAILS_BUILD_IGNORE_CHANGES']
137       $stderr.puts <<-END_OF_MESSAGE.gsub(/^        /, '')
139         You have uncommited changes in the Git repository. They will
140         be ignored for the upcoming build.
142       END_OF_MESSAGE
143     else
144       $stderr.puts <<-END_OF_MESSAGE.gsub(/^        /, '')
146         You have uncommited changes in the Git repository. Due to limitations
147         of the build system, you need to commit them before building Tails.
149         If you don't care about those changes and want to build Tails nonetheless,
150         please add `ignorechanges` to the TAILS_BUILD_OPTIONS environment
151         variable.
153       END_OF_MESSAGE
154       abort 'Uncommited changes. Aborting.'
155     end
156   end
159 task :validate_http_proxy do
160   if ENV['http_proxy']
161     proxy_host = URI.parse(ENV['http_proxy']).host
163     if proxy_host.nil?
164       ENV['http_proxy'] = nil
165       $stderr.puts "Ignoring invalid HTTP proxy."
166       return
167     end
169     if ['localhost', '[::1]'].include?(proxy_host) || proxy_host.start_with?('127.0.0.')
170       abort 'Using an HTTP proxy listening on the loopback is doomed to fail. Aborting.'
171     end
173     $stderr.puts "Using HTTP proxy: #{ENV['http_proxy']}"
174   else
175     $stderr.puts "No HTTP proxy set."
176   end
179 desc 'Build Tails'
180 task :build => ['parse_build_options', 'ensure_clean_repository', 'validate_http_proxy', 'vm:up'] do
181   exported_env = EXPORTED_VARIABLES.select { |k| ENV[k] }.
182                   collect { |k| "#{k}='#{ENV[k]}'" }.join(' ')
184   env = Vagrant::Environment.new(:cwd => VAGRANT_PATH)
185   status = env.primary_vm.channel.execute("#{exported_env} build-tails",
186                                           :error_check => false) do |fd, data|
187     (fd == :stdout ? $stdout : $stderr).write data
188   end
190   # Move build products to the current directory
191   FileUtils.mv Dir.glob("#{VAGRANT_PATH}/tails-*"),
192                File.expand_path('..', __FILE__), :force => true
194   exit status
197 namespace :vm do
198   desc 'Start the build virtual machine'
199   task :up => ['parse_build_options', 'validate_http_proxy'] do
200     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
201     case env.primary_vm.state
202     when :not_created
203       # Do not use non-existant in-VM proxy to download the basebox
204       if ENV['http_proxy'] == INTERNEL_HTTP_PROXY
205         ENV['http_proxy'] = nil
206         restore_internal_proxy = true
207       end
209       $stderr.puts <<-END_OF_MESSAGE.gsub(/^      /, '')
211         This is the first time that the Tails builder virtual machine is
212         started. The virtual machine template is about 300 MB to download,
213         so the process might take some time.
215         Please remember to shut the virtual machine down once your work on
216         Tails in done:
218             $ rake vm:halt
220       END_OF_MESSAGE
221     when :poweroff
222       $stderr.puts <<-END_OF_MESSAGE.gsub(/^      /, '')
224         Starting Tails builder virtual machine. This might take a short while.
225         Please remember to shut it down once your work on Tails in done:
227             $ rake vm:halt
229       END_OF_MESSAGE
230     when :running
231       if ENV['TAILS_RAM_BUILD'] && current_vm_memory < VM_MEMORY_FOR_RAM_BUILDS
232         $stderr.puts <<-END_OF_MESSAGE.gsub(/^          /, '')
234           The virtual machine is not currently set with enough memory to
235           perform an in-memory build. Either remove the `ram` option from
236           the TAILS_BUILD_OPTIONS environment variable, or shut the
237           virtual machine down using `rake vm:halt` before trying again.
239         END_OF_MESSAGE
240         abort 'Not enough memory for the virtual machine to run an in-memory build. Aborting.'
241       end
242       if ENV['TAILS_BUILD_CPUS'] && current_vm_cpus != ENV['TAILS_BUILD_CPUS'].to_i
243         $stderr.puts <<-END_OF_MESSAGE.gsub(/^          /, '')
245           The virtual machine is currently running with #{current_vm_cpus}
246           virtual CPU(s). In order to change that number, you need to
247           stop the VM first, using `rake vm:halt`. Otherwise, please
248           adjust the `cpus` options accordingly.
250         END_OF_MESSAGE
251         abort 'The virtual machine needs to be reloaded to change the number of CPUs. Aborting.'
252       end
253     end
254     result = env.cli('up')
255     abort "'vagrant up' failed" unless result
257     ENV['http_proxy'] = INTERNEL_HTTP_PROXY if restore_internal_proxy
258   end
260   desc 'Stop the build virtual machine'
261   task :halt do
262     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
263     result = env.cli('halt')
264     abort "'vagrant halt' failed" unless result
265   end
267   desc 'Re-run virtual machine setup'
268   task :provision => ['parse_build_options', 'validate_http_proxy'] do
269     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
270     result = env.cli('provision')
271     abort "'vagrant provision' failed" unless result
272   end
274   desc 'Destroy build virtual machine (clean up all files)'
275   task :destroy do
276     env = Vagrant::Environment.new(:cwd => VAGRANT_PATH, :ui_class => Vagrant::UI::Basic)
277     result = env.cli('destroy', '--force')
278     abort "'vagrant destroy' failed" unless result
279   end
282 namespace :basebox do
283   task :create_preseed_cfg => 'validate_http_proxy' do
284     require 'erb'
286     preseed_cfg_path = File.expand_path('../vagrant/definitions/squeeze/preseed.cfg', __FILE__)
287     template = ERB.new(File.read("#{preseed_cfg_path}.erb"))
288     File.open(preseed_cfg_path, 'w') do |f|
289       f.write template.result
290     end
291   end
293   desc 'Create virtual machine template (a.k.a. basebox)'
294   task :create_basebox => [:create_preseed_cfg] do
295     # veewee is pretty stupid regarding path handling
296     Dir.chdir(VAGRANT_PATH) do
297       require 'veewee'
299       # Veewee assumes a separate process for each task. So we mimic that.
301       env = Vagrant::Environment.new(:ui_class => Vagrant::UI::Basic)
303       Process.fork do
304         env.cli('basebox', 'build', 'squeeze')
305       end
306       Process.wait
307       abort "Building the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
309       Process.fork do
310         env.cli('basebox', 'validate', 'squeeze')
311       end
312       Process.wait
313       abort "Validating the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
315       Process.fork do
316         env.cli('basebox', 'export', 'squeeze')
317       end
318       Process.wait
319       abort "Exporting the basebox failed (exit code: #{$?.exitstatus})." if $?.exitstatus != 0
320     end
321   end