Commit 99c90a64 by Andy Armstrong

Support compiling SASS for only one system

parent c13e6230
source 'https://rubygems.org'
gem 'sass', '3.3.5'
gem 'bourbon', '~> 4.0.2'
gem 'neat', '~> 1.6.0'
......@@ -7,7 +7,7 @@ GEM
neat (1.6.0)
bourbon (>= 3.1)
sass (>= 3.3)
sass (3.3.5)
sass (3.4.21)
thor (0.19.1)
PLATFORMS
......@@ -16,4 +16,3 @@ PLATFORMS
DEPENDENCIES
bourbon (~> 4.0.2)
neat (~> 1.6.0)
sass (= 3.3.5)
......@@ -29,6 +29,7 @@ dependencies:
# Install a version which falls within that range.
- pip install --exists-action w pbr==0.9.0
- pip install --exists-action w -r requirements/edx/base.txt
- pip install --exists-action w -r requirements/edx/paver.txt
- if [ -e requirements/edx/post.txt ]; then pip install --exists-action w -r requirements/edx/post.txt ; fi
- pip install coveralls==1.0
......
......@@ -30,7 +30,7 @@ __test__ = False # do not collect
])
def test_acceptance(options):
"""
Run the acceptance tests for the either lms or cms
Run the acceptance tests for either lms or cms
"""
opts = {
'fasttest': getattr(options, 'fasttest', False),
......
......@@ -12,24 +12,28 @@ from paver import tasks
from paver.easy import sh, path, task, cmdopts, needs, consume_args, call_task, no_help
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import sass
from .utils.envs import Env
from .utils.cmd import cmd, django_cmd
# setup baseline paths
ALL_SYSTEMS = ['lms', 'studio']
COFFEE_DIRS = ['lms', 'cms', 'common']
# A list of directories. Each will be paired with a sibling /css directory.
SASS_DIRS = [
COMMON_SASS_DIRECTORIES = [
path("common/static/sass"),
]
LMS_SASS_DIRECTORIES = [
path("lms/static/sass"),
path("lms/static/themed_sass"),
path("cms/static/sass"),
path("common/static/sass"),
path("lms/static/certificates/sass"),
]
CMS_SASS_DIRECTORIES = [
path("cms/static/sass"),
]
THEME_SASS_DIRECTORIES = []
SASS_LOAD_PATHS = ['common/static', 'common/static/sass']
SASS_CACHE_PATH = '/tmp/sass-cache'
def configure_paths():
......@@ -44,7 +48,7 @@ def configure_paths():
css_dir = theme_root / "static" / "css"
if sass_dir.isdir():
css_dir.mkdir_p()
SASS_DIRS.append(sass_dir)
THEME_SASS_DIRECTORIES.append(sass_dir)
if edxapp_env.env_tokens.get("COMPREHENSIVE_THEME_DIR", ""):
theme_dir = path(edxapp_env.env_tokens["COMPREHENSIVE_THEME_DIR"])
......@@ -52,16 +56,39 @@ def configure_paths():
lms_css = theme_dir / "lms" / "static" / "css"
if lms_sass.isdir():
lms_css.mkdir_p()
SASS_DIRS.append(lms_sass)
THEME_SASS_DIRECTORIES.append(lms_sass)
cms_sass = theme_dir / "cms" / "static" / "sass"
cms_css = theme_dir / "cms" / "static" / "css"
if cms_sass.isdir():
cms_css.mkdir_p()
SASS_DIRS.append(cms_sass)
THEME_SASS_DIRECTORIES.append(cms_sass)
configure_paths()
def applicable_sass_directories(systems=None):
"""
Determine the applicable set of SASS directories to be
compiled for the specified list of systems.
Args:
systems: A list of systems (defaults to all)
Returns:
A list of SASS directories to be compiled.
"""
if not systems:
systems = ALL_SYSTEMS
applicable_directories = []
applicable_directories.extend(COMMON_SASS_DIRECTORIES)
if "lms" in systems:
applicable_directories.extend(LMS_SASS_DIRECTORIES)
if "studio" in systems or "cms" in systems:
applicable_directories.extend(CMS_SASS_DIRECTORIES)
applicable_directories.extend(THEME_SASS_DIRECTORIES)
return applicable_directories
class CoffeeScriptWatcher(PatternMatchingEventHandler):
"""
Watches for coffeescript changes
......@@ -99,7 +126,7 @@ class SassWatcher(PatternMatchingEventHandler):
"""
register files with observer
"""
for dirname in SASS_LOAD_PATHS + SASS_DIRS:
for dirname in SASS_LOAD_PATHS + applicable_sass_directories():
paths = []
if '*' in dirname:
paths.extend(glob.glob(dirname))
......@@ -185,6 +212,7 @@ def compile_coffeescript(*files):
@task
@no_help
@cmdopts([
('system=', 's', 'The system to compile sass for (defaults to all)'),
('debug', 'd', 'Debug mode'),
('force', '', 'Force full compilation'),
])
......@@ -192,8 +220,18 @@ def compile_sass(options):
"""
Compile Sass to CSS.
"""
debug = options.get('debug')
# Note: import sass only when it is needed and not at the top of the file.
# This allows other paver commands to operate even without libsass being
# installed. In particular, this allows the install_prereqs command to be
# used to install the dependency.
import sass
debug = options.get('debug')
force = options.get('force')
systems = getattr(options, 'system', ALL_SYSTEMS)
if isinstance(systems, basestring):
systems = systems.split(',')
if debug:
source_comments = True
output_style = 'nested'
......@@ -202,22 +240,39 @@ def compile_sass(options):
output_style = 'compressed'
timing_info = []
for sass_dir in SASS_DIRS:
system_sass_directories = applicable_sass_directories(systems)
all_sass_directories = applicable_sass_directories()
dry_run = tasks.environment.dry_run
for sass_dir in system_sass_directories:
start = datetime.now()
css_dir = sass_dir.parent / "css"
sass.compile(
dirname=(sass_dir, css_dir),
include_paths=SASS_LOAD_PATHS + SASS_DIRS,
source_comments=source_comments,
output_style=output_style,
)
duration = datetime.now() - start
timing_info.append((sass_dir, css_dir, duration))
if force:
if dry_run:
tasks.environment.info("rm -rf {css_dir}/*.css".format(
css_dir=css_dir,
))
else:
sh("rm -rf {css_dir}/*.css".format(css_dir=css_dir))
if dry_run:
tasks.environment.info("libsass {sass_dir}".format(
sass_dir=sass_dir,
))
else:
sass.compile(
dirname=(sass_dir, css_dir),
include_paths=SASS_LOAD_PATHS + all_sass_directories,
source_comments=source_comments,
output_style=output_style,
)
duration = datetime.now() - start
timing_info.append((sass_dir, css_dir, duration))
print("\t\tFinished compiling Sass:")
for sass_dir, css_dir, duration in timing_info:
print(">> {} -> {} in {}s".format(sass_dir, css_dir, duration))
if not dry_run:
for sass_dir, css_dir, duration in timing_info:
print(">> {} -> {} in {}s".format(sass_dir, css_dir, duration))
def compile_templated_sass(systems, settings):
......@@ -226,15 +281,15 @@ def compile_templated_sass(systems, settings):
`systems` is a list of systems (e.g. 'lms' or 'studio' or both)
`settings` is the Django settings module to use.
"""
for sys in systems:
if sys == "studio":
sys = "cms"
for system in systems:
if system == "studio":
system = "cms"
sh(django_cmd(
sys, settings, 'preprocess_assets',
'{sys}/static/sass/*.scss'.format(sys=sys),
'{sys}/static/themed_sass'.format(sys=sys)
system, settings, 'preprocess_assets',
'{system}/static/sass/*.scss'.format(system=system),
'{system}/static/themed_sass'.format(system=system)
))
print("\t\tFinished preprocessing {} assets.".format(sys))
print("\t\tFinished preprocessing {} assets.".format(system))
def process_xmodule_assets():
......@@ -310,7 +365,7 @@ def update_assets(args):
"""
parser = argparse.ArgumentParser(prog='paver update_assets')
parser.add_argument(
'system', type=str, nargs='*', default=['lms', 'studio'],
'system', type=str, nargs='*', default=ALL_SYSTEMS,
help="lms or studio",
)
parser.add_argument(
......@@ -334,7 +389,7 @@ def update_assets(args):
compile_templated_sass(args.system, args.settings)
process_xmodule_assets()
compile_coffeescript()
call_task('pavelib.assets.compile_sass', options={'debug': args.debug})
call_task('pavelib.assets.compile_sass', options={'system': args.system, 'debug': args.debug})
if args.collect:
collect_assets(args.system, args.settings)
......
"""Unit tests for the Paver asset tasks."""
import ddt
from paver.easy import call_task
from .utils import PaverTestCase
@ddt.ddt
class TestPaverAssetTasks(PaverTestCase):
"""
Test the Paver asset tasks.
"""
@ddt.data(
[""],
["--force"],
["--debug"],
["--system=lms"],
["--system=lms --force"],
["--system=studio"],
["--system=studio --force"],
["--system=lms,studio"],
["--system=lms,studio --force"],
)
@ddt.unpack
def test_compile_sass(self, options):
"""
Test the "compile_sass" task.
"""
parameters = options.split(" ")
system = []
if "--system=studio" not in parameters:
system += ["lms"]
if "--system=lms" not in parameters:
system += ["studio"]
debug = "--debug" in parameters
force = "--force" in parameters
self.reset_task_messages()
call_task('pavelib.assets.compile_sass', options={"system": system, "debug": debug, "force": force})
expected_messages = []
if force:
expected_messages.append("rm -rf common/static/css/*.css")
expected_messages.append("libsass common/static/sass")
if "lms" in system:
if force:
expected_messages.append("rm -rf lms/static/css/*.css")
expected_messages.append("libsass lms/static/sass")
if force:
expected_messages.append("rm -rf lms/static/css/*.css")
expected_messages.append("libsass lms/static/themed_sass")
if force:
expected_messages.append("rm -rf lms/static/certificates/css/*.css")
expected_messages.append("libsass lms/static/certificates/sass")
if "studio" in system:
if force:
expected_messages.append("rm -rf cms/static/css/*.css")
expected_messages.append("libsass cms/static/sass")
self.assertEquals(self.task_messages, expected_messages)
......@@ -11,21 +11,19 @@ EXPECTED_COFFEE_COMMAND = (
"{platform_root}/cms {platform_root}/common -type f -name \"*.coffee\"`"
)
EXPECTED_SASS_COMMAND = (
"sass --update --cache-location /tmp/sass-cache --default-encoding utf-8 --style compressed"
" --quiet"
" --load-path ."
" --load-path common/static"
" --load-path common/static/sass"
" --load-path lms/static/sass"
" --load-path lms/static/themed_sass"
" --load-path cms/static/sass --load-path common/static/sass"
" --load-path lms/static/certificates/sass"
" lms/static/sass:lms/static/css"
" lms/static/themed_sass:lms/static/css"
" cms/static/sass:cms/static/css"
" common/static/sass:common/static/css"
" lms/static/certificates/sass:lms/static/certificates/css"
"libsass {sass_directory}"
)
EXPECTED_COMMON_SASS_DIRECTORIES = [
"common/static/sass",
]
EXPECTED_LMS_SASS_DIRECTORIES = [
"lms/static/sass",
"lms/static/themed_sass",
"lms/static/certificates/sass",
]
EXPECTED_CMS_SASS_DIRECTORIES = [
"cms/static/sass",
]
EXPECTED_PREPROCESS_ASSETS_COMMAND = (
"python manage.py {system} --settings={asset_settings} preprocess_assets"
" {system}/static/sass/*.scss {system}/static/themed_sass"
......@@ -236,7 +234,7 @@ class TestPaverServerTasks(PaverTestCase):
))
expected_messages.append("xmodule_assets common/static/xmodule")
expected_messages.append(EXPECTED_COFFEE_COMMAND.format(platform_root=platform_root))
expected_messages.append(EXPECTED_SASS_COMMAND)
expected_messages.extend(self.expected_sass_commands(system=system))
if expected_collect_static:
expected_messages.append(EXPECTED_COLLECT_STATIC_COMMAND.format(
system=system, asset_settings=expected_asset_settings
......@@ -278,7 +276,7 @@ class TestPaverServerTasks(PaverTestCase):
))
expected_messages.append("xmodule_assets common/static/xmodule")
expected_messages.append(EXPECTED_COFFEE_COMMAND.format(platform_root=platform_root))
expected_messages.append(EXPECTED_SASS_COMMAND)
expected_messages.extend(self.expected_sass_commands())
if expected_collect_static:
expected_messages.append(EXPECTED_COLLECT_STATIC_COMMAND.format(
system="lms", asset_settings=expected_asset_settings
......@@ -302,3 +300,15 @@ class TestPaverServerTasks(PaverTestCase):
)
expected_messages.append(EXPECTED_CELERY_COMMAND.format(settings="dev_with_worker"))
self.assertEquals(self.task_messages, expected_messages)
def expected_sass_commands(self, system=None):
"""
Returns the expected SASS commands for the specified system.
"""
expected_sass_directories = []
expected_sass_directories.extend(EXPECTED_COMMON_SASS_DIRECTORIES)
if system != 'cms':
expected_sass_directories.extend(EXPECTED_LMS_SASS_DIRECTORIES)
if system != 'lms':
expected_sass_directories.extend(EXPECTED_CMS_SASS_DIRECTORIES)
return [EXPECTED_SASS_COMMAND.format(sass_directory=directory) for directory in expected_sass_directories]
......@@ -24,6 +24,7 @@ PYTHON_REQ_FILES = [
'requirements/edx/github.txt',
'requirements/edx/local.txt',
'requirements/edx/base.txt',
'requirements/edx/paver.txt',
'requirements/edx/post.txt',
]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment