rakefile 10.2 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 172 173 174 175

    desc "Run tests for common lib #{lib} (without coverage)"
    task "fasttest_#{lib}" do 
        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 312 313 314 315 316 317 318 319 320 321 322 323

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

desc "Show doc in browser (mac only for now) TODO add linux support"
task :showdocs do
    Dir.chdir('docs/build/html') do
        sh('open index.html')
    end
end

desc "Build docs and show them in browser"
task :doc => :builddocs do
    Dir.chdir('docs/build/html') do
        sh('open index.html')
    end
324
end