rakefile 10.5 KB
Newer Older
1 2 3
require 'rake/clean'
require 'tempfile'

4
# Build Constants
5 6
REPO_ROOT = File.dirname(__FILE__)
BUILD_DIR = File.join(REPO_ROOT, "build")
7
REPORT_DIR = File.join(REPO_ROOT, "reports")
8
LMS_REPORT_DIR = File.join(REPORT_DIR, "lms")
9 10

# Packaging constants
11
DEPLOY_DIR = "/opt/wwc"
12
PACKAGE_NAME = "mitx"
13
LINK_PATH = "/opt/wwc/mitx"
14
PKG_VERSION = "0.1"
15
COMMIT = (ENV["GIT_COMMIT"] || `git rev-parse HEAD`).chomp()[0, 10]
16
BRANCH = (ENV["GIT_BRANCH"] || `git symbolic-ref -q HEAD`).chomp().gsub('refs/heads/', '').gsub('origin/', '')
17 18 19
BUILD_NUMBER = (ENV["BUILD_NUMBER"] || "dev").chomp()

if BRANCH == "master"
20
    DEPLOY_NAME = "#{PACKAGE_NAME}-#{BUILD_NUMBER}-#{COMMIT}"
21
else
22
    DEPLOY_NAME = "#{PACKAGE_NAME}-#{BRANCH}-#{BUILD_NUMBER}-#{COMMIT}"
23 24 25
end
PACKAGE_REPO = "packages@gp.mitx.mit.edu:/opt/pkgrepo.incoming"

26 27
NORMALIZED_DEPLOY_NAME = DEPLOY_NAME.downcase().gsub(/[_\/]/, '-')
INSTALL_DIR_PATH = File.join(DEPLOY_DIR, NORMALIZED_DEPLOY_NAME)
28
# Set up the clean and clobber tasks
29
CLOBBER.include(BUILD_DIR, REPORT_DIR, 'test_root/*_repo', 'test_root/staticfiles')
30
CLEAN.include("#{BUILD_DIR}/*.deb", "#{BUILD_DIR}/util")
31

32 33 34 35
def select_executable(*cmds)
    cmds.find_all{ |cmd| system("which #{cmd} > /dev/null 2>&1") }[0] || fail("No executables found from #{cmds.join(', ')}")
end

36 37
def django_admin(system, env, command, *args)
    django_admin = ENV['DJANGO_ADMIN_PATH'] || select_executable('django-admin.py', 'django-admin')
38
    return "#{django_admin} #{command} --traceback --settings=#{system}.envs.#{env} --pythonpath=. #{args.join(' ')}"
39
end
40

41 42 43 44
def report_dir_path(dir)
    return File.join(REPORT_DIR, dir.to_s)
end

45
task :default => [:test, :pep8, :pylint]
46 47

directory REPORT_DIR
Calen Pennington committed
48

49 50 51 52 53
default_options = {
    :lms => '8000',
    :cms => '8001',
}

54 55
task :predjango do
    sh("find . -type f -name *.pyc -delete")
56
    sh('pip install -q --upgrade --no-deps -r local-requirements.txt')
57 58
end

59
task :clean_test_files do
60
    sh("git clean -fqdx test_root")
61 62
end

63
[:lms, :cms, :common].each do |system|
64
    report_dir = report_dir_path(system)
65 66 67 68 69 70 71 72 73 74 75
    directory report_dir

    desc "Run pep8 on all #{system} code"
    task "pep8_#{system}" => report_dir do
        sh("pep8 --ignore=E501 #{system}/djangoapps #{system}/lib | tee #{report_dir}/pep8.report")
    end
    task :pep8 => "pep8_#{system}"

    desc "Run pylint on all #{system} code"
    task "pylint_#{system}" => report_dir do
        Dir["#{system}/djangoapps/*", "#{system}/lib/*"].each do |app|
76 77 78 79 80
            if File.exists? "#{app}/setup.py"
                pythonpath_prefix = "PYTHONPATH=#{app}"
            else
                pythonpath_prefix = "PYTHONPATH=#{File.dirname(app)}"
            end
81
            app = File.basename(app)
82
            sh("#{pythonpath_prefix} pylint --rcfile=.pylintrc -f parseable #{app} | tee #{report_dir}/#{app}.pylint.report")
83 84 85 86 87
        end
    end
    task :pylint => "pylint_#{system}"
end

88
$failed_tests = 0
89

90
def run_under_coverage(cmd, root)
91
    cmd0, cmd_rest = cmd.split(" ", 2)
92 93 94
    # We use "python -m coverage" so that the proper python will run the importable coverage
    # rather than the coverage that OS path finds.
    cmd = "python -m coverage run --rcfile=#{root}/.coveragerc `which #{cmd0}` #{cmd_rest}"
95 96 97
    return cmd
end

98
def run_tests(system, report_dir, stop_on_failure=true)
99
    ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml")
100
    dirs = Dir["common/djangoapps/*"] + Dir["#{system}/djangoapps/*"]
101
    cmd = django_admin(system, :test, 'test', '--logging-clear-handlers', *dirs.each)
102
    sh(run_under_coverage(cmd, system)) do |ok, res|
103 104 105 106 107
        if !ok and stop_on_failure
            abort "Test failed!"
        end
        $failed_tests += 1 unless ok
    end
108 109
end

110
TEST_TASK_DIRS = []
111

112
[:lms, :cms].each do |system|
113
    report_dir = report_dir_path(system)
114

115
    # Per System tasks
116
    desc "Run all django tests on our djangoapps for the #{system}"
117
    task "test_#{system}", [:stop_on_failure] => ["clean_test_files", "#{system}:collectstatic:test", "fasttest_#{system}"]
118

119 120
    # Have a way to run the tests without running collectstatic -- useful when debugging without
    # messing with static files.
121 122 123
    task "fasttest_#{system}", [:stop_on_failure] => [report_dir, :predjango] do |t, args|
        args.with_defaults(:stop_on_failure => 'true')
        run_tests(system, report_dir, args.stop_on_failure)
124
    end
125

126
    TEST_TASK_DIRS << system
127 128 129 130 131

    desc <<-desc
        Start the #{system} locally with the specified environment (defaults to dev).
        Other useful environments are devplus (for dev testing with a real local database)
        desc
132
    task system, [:env, :options] => [:predjango] do |t, args|
133
        args.with_defaults(:env => 'dev', :options => default_options[system])
134 135
        sh(django_admin(system, args.env, 'runserver', args.options))
    end
136 137

    # Per environment tasks
138 139
    Dir["#{system}/envs/**/*.py"].each do |env_file|
        env = env_file.gsub("#{system}/envs/", '').gsub(/\.py/, '').gsub('/', '.')
140
        desc "Attempt to import the settings file #{system}.envs.#{env} and report any errors"
141
        task "#{system}:check_settings:#{env}" => :predjango do
142 143
            sh("echo 'import #{system}.envs.#{env}' | #{django_admin(system, env, 'shell')}")
        end
144 145 146

        desc "Run collectstatic in the specified environment"
        task "#{system}:collectstatic:#{env}" => :predjango do
147 148 149 150 151
            sh("#{django_admin(system, env, 'collectstatic', '--noinput')} > /tmp/collectstatic.out") do |ok, status|
                if !ok
                    abort "collectstatic failed!"
                end
            end
152
        end
153
    end
154 155
end

156
Dir["common/lib/*"].select{|lib| File.directory?(lib)}.each do |lib|
157 158
    task_name = "test_#{lib}"

159
    report_dir = report_dir_path(lib)
160 161

    desc "Run tests for common lib #{lib}"
162
    task task_name => report_dir do
163
        ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml")
164
        cmd = "nosetests #{lib} --logging-clear-handlers --with-xunit"
165 166 167
        sh(run_under_coverage(cmd, lib)) do |ok, res|
            $failed_tests += 1 unless ok
        end
168
    end
169
    TEST_TASK_DIRS << lib
Victor Shnayder committed
170 171

    desc "Run tests for common lib #{lib} (without coverage)"
172
    task "fasttest_#{lib}" do
Victor Shnayder committed
173 174 175
        sh("nosetests #{lib}")
    end

176 177
end

178 179 180 181 182 183 184 185
task :report_dirs

TEST_TASK_DIRS.each do |dir|
    report_dir = report_dir_path(dir)
    directory report_dir
    task :report_dirs => [REPORT_DIR, report_dir]
end

186
task :test do
187 188
    TEST_TASK_DIRS.each do |dir|
        Rake::Task["test_#{dir}"].invoke(false)
189 190 191 192 193
    end

    if $failed_tests > 0
        abort "Tests failed!"
    end
194
end
195

196 197
namespace :coverage do
    desc "Build the html coverage reports"
198
    task :html => :report_dirs do
199
        TEST_TASK_DIRS.each do |dir|
200 201 202 203 204 205 206
            report_dir = report_dir_path(dir)

            if !File.file?("#{report_dir}/.coverage")
                next
            end

            sh("coverage html --rcfile=#{dir}/.coveragerc")
207
        end
208 209
    end

210
    desc "Build the xml coverage reports"
211
    task :xml => :report_dirs do
212
        TEST_TASK_DIRS.each do |dir|
213 214 215 216 217
            report_dir = report_dir_path(dir)

            if !File.file?("#{report_dir}/.coverage")
                next
            end
218
            # Why doesn't the rcfile control the xml output file properly??
219
            sh("coverage xml -o #{report_dir}/coverage.xml --rcfile=#{dir}/.coveragerc")
220
        end
221
    end
222 223
end

224 225
task :runserver => :lms

226
desc "Run django-admin <action> against the specified system and environment"
227
task "django-admin", [:action, :system, :env, :options] => [:predjango] do |t, args|
228 229 230 231
    args.with_defaults(:env => 'dev', :system => 'lms', :options => '')
    sh(django_admin(args.system, args.env, args.action, args.options))
end

232 233 234 235 236 237
desc "Set the staff bit for a user"
task :set_staff, [:user, :system, :env] do |t, args|
    args.with_defaults(:env => 'dev', :system => 'lms', :options => '')
    sh(django_admin(args.system, args.env, 'set_staff', args.user))
end

238
task :package do
239
    FileUtils.mkdir_p(BUILD_DIR)
240

241
    Dir.chdir(BUILD_DIR) do
242 243
        afterremove = Tempfile.new('afterremove')
        afterremove.write <<-AFTERREMOVE.gsub(/^\s*/, '')
John Jarvis committed
244
        #! /bin/bash
245 246
        set -e
        set -x
247

248
        # to be a little safer this rm is executed
John Jarvis committed
249
        # as the makeitso user
250

John Jarvis committed
251
        if [[ -d "#{INSTALL_DIR_PATH}" ]]; then
252
            sudo rm -rf "#{INSTALL_DIR_PATH}"
253 254 255 256 257
        fi

        AFTERREMOVE
        afterremove.close()
        FileUtils.chmod(0755, afterremove.path)
258

259
        args = ["fakeroot", "fpm", "-s", "dir", "-t", "deb",
260
            "--after-remove=#{afterremove.path}",
261
            "--prefix=#{INSTALL_DIR_PATH}",
262 263 264
            "--exclude=**/build/**",
            "--exclude=**/rakefile",
            "--exclude=**/.git/**",
265
            "--exclude=**/*.pyc",
266
            "--exclude=**/reports/**",
267 268
            "--exclude=**/test_root/**",
            "--exclude=**/.coverage/**",
269
            "-C", "#{REPO_ROOT}",
270
            "--provides=#{PACKAGE_NAME}",
271
            "--name=#{NORMALIZED_DEPLOY_NAME}",
272
            "--version=#{PKG_VERSION}",
273
            "-a", "all",
274
            "."]
275 276 277
        system(*args) || raise("fpm failed to build the .deb")
    end
end
278 279

task :publish => :package do
280
    sh("scp #{BUILD_DIR}/#{NORMALIZED_DEPLOY_NAME}_#{PKG_VERSION}*.deb #{PACKAGE_REPO}")
281
end
282 283 284 285 286 287 288 289 290 291 292 293

namespace :cms do
  desc "Import course data within the given DATA_DIR variable"
  task :import do
    if ENV['DATA_DIR']
      sh(django_admin(:cms, :dev, :import, ENV['DATA_DIR']))
    else
      raise "Please specify a DATA_DIR variable that point to your data directory.\n" +
        "Example: \`rake cms:import DATA_DIR=../data\`"
    end
  end
end
294 295 296 297 298 299 300 301 302 303

desc "Build a properties file used to trigger autodeploy builds"
task :autodeploy_properties do
    File.open("autodeploy.properties", "w") do |file|
        file.puts("UPSTREAM_NOOP=false")
        file.puts("UPSTREAM_BRANCH=#{BRANCH}")
        file.puts("UPSTREAM_JOB=#{PACKAGE_NAME}")
        file.puts("UPSTREAM_REVISION=#{COMMIT}")
    end
end
304 305 306 307 308 309 310 311

desc "Invoke sphinx 'make build' to generate docs."
task :builddocs do
    Dir.chdir('docs') do
        sh('make html')
    end
end

312
desc "Show docs in browser (mac and ubuntu)."
313 314
task :showdocs do
    Dir.chdir('docs/build/html') do
315 316 317
        if RUBY_PLATFORM.include? 'darwin'  #  mac os
            sh('open index.html')
        elsif RUBY_PLATFORM.include? 'linux'  # make more ubuntu specific?
Alexander Kryklia committed
318
            sh('sensible-browser index.html')  # ubuntu
319 320 321 322 323
        else
            raise "\nUndefined how to run browser on your machine.
Please use 'rake builddocs' and then manually open
'mitx/docs/build/html/index.html."
        end
324 325 326 327 328
    end
end

desc "Build docs and show them in browser"
task :doc => :builddocs do
329
    Rake::Task["showdocs"].invoke
330
end