Commit 99c90a64 by Andy Armstrong

Support compiling SASS for only one system

parent c13e6230
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'sass', '3.3.5'
gem 'bourbon', '~> 4.0.2' gem 'bourbon', '~> 4.0.2'
gem 'neat', '~> 1.6.0' gem 'neat', '~> 1.6.0'
...@@ -7,7 +7,7 @@ GEM ...@@ -7,7 +7,7 @@ GEM
neat (1.6.0) neat (1.6.0)
bourbon (>= 3.1) bourbon (>= 3.1)
sass (>= 3.3) sass (>= 3.3)
sass (3.3.5) sass (3.4.21)
thor (0.19.1) thor (0.19.1)
PLATFORMS PLATFORMS
...@@ -16,4 +16,3 @@ PLATFORMS ...@@ -16,4 +16,3 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
bourbon (~> 4.0.2) bourbon (~> 4.0.2)
neat (~> 1.6.0) neat (~> 1.6.0)
sass (= 3.3.5)
...@@ -29,6 +29,7 @@ dependencies: ...@@ -29,6 +29,7 @@ dependencies:
# Install a version which falls within that range. # Install a version which falls within that range.
- pip install --exists-action w pbr==0.9.0 - 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/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 - if [ -e requirements/edx/post.txt ]; then pip install --exists-action w -r requirements/edx/post.txt ; fi
- pip install coveralls==1.0 - pip install coveralls==1.0
......
...@@ -30,7 +30,7 @@ __test__ = False # do not collect ...@@ -30,7 +30,7 @@ __test__ = False # do not collect
]) ])
def test_acceptance(options): def test_acceptance(options):
""" """
Run the acceptance tests for the either lms or cms Run the acceptance tests for either lms or cms
""" """
opts = { opts = {
'fasttest': getattr(options, 'fasttest', False), 'fasttest': getattr(options, 'fasttest', False),
......
...@@ -12,24 +12,28 @@ from paver import tasks ...@@ -12,24 +12,28 @@ from paver import tasks
from paver.easy import sh, path, task, cmdopts, needs, consume_args, call_task, no_help from paver.easy import sh, path, task, cmdopts, needs, consume_args, call_task, no_help
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler from watchdog.events import PatternMatchingEventHandler
import sass
from .utils.envs import Env from .utils.envs import Env
from .utils.cmd import cmd, django_cmd from .utils.cmd import cmd, django_cmd
# setup baseline paths # setup baseline paths
ALL_SYSTEMS = ['lms', 'studio']
COFFEE_DIRS = ['lms', 'cms', 'common'] COFFEE_DIRS = ['lms', 'cms', 'common']
# A list of directories. Each will be paired with a sibling /css directory. # 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/sass"),
path("lms/static/themed_sass"), path("lms/static/themed_sass"),
path("cms/static/sass"),
path("common/static/sass"),
path("lms/static/certificates/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_LOAD_PATHS = ['common/static', 'common/static/sass']
SASS_CACHE_PATH = '/tmp/sass-cache'
def configure_paths(): def configure_paths():
...@@ -44,7 +48,7 @@ def configure_paths(): ...@@ -44,7 +48,7 @@ def configure_paths():
css_dir = theme_root / "static" / "css" css_dir = theme_root / "static" / "css"
if sass_dir.isdir(): if sass_dir.isdir():
css_dir.mkdir_p() css_dir.mkdir_p()
SASS_DIRS.append(sass_dir) THEME_SASS_DIRECTORIES.append(sass_dir)
if edxapp_env.env_tokens.get("COMPREHENSIVE_THEME_DIR", ""): if edxapp_env.env_tokens.get("COMPREHENSIVE_THEME_DIR", ""):
theme_dir = path(edxapp_env.env_tokens["COMPREHENSIVE_THEME_DIR"]) theme_dir = path(edxapp_env.env_tokens["COMPREHENSIVE_THEME_DIR"])
...@@ -52,16 +56,39 @@ def configure_paths(): ...@@ -52,16 +56,39 @@ def configure_paths():
lms_css = theme_dir / "lms" / "static" / "css" lms_css = theme_dir / "lms" / "static" / "css"
if lms_sass.isdir(): if lms_sass.isdir():
lms_css.mkdir_p() lms_css.mkdir_p()
SASS_DIRS.append(lms_sass) THEME_SASS_DIRECTORIES.append(lms_sass)
cms_sass = theme_dir / "cms" / "static" / "sass" cms_sass = theme_dir / "cms" / "static" / "sass"
cms_css = theme_dir / "cms" / "static" / "css" cms_css = theme_dir / "cms" / "static" / "css"
if cms_sass.isdir(): if cms_sass.isdir():
cms_css.mkdir_p() cms_css.mkdir_p()
SASS_DIRS.append(cms_sass) THEME_SASS_DIRECTORIES.append(cms_sass)
configure_paths() 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): class CoffeeScriptWatcher(PatternMatchingEventHandler):
""" """
Watches for coffeescript changes Watches for coffeescript changes
...@@ -99,7 +126,7 @@ class SassWatcher(PatternMatchingEventHandler): ...@@ -99,7 +126,7 @@ class SassWatcher(PatternMatchingEventHandler):
""" """
register files with observer register files with observer
""" """
for dirname in SASS_LOAD_PATHS + SASS_DIRS: for dirname in SASS_LOAD_PATHS + applicable_sass_directories():
paths = [] paths = []
if '*' in dirname: if '*' in dirname:
paths.extend(glob.glob(dirname)) paths.extend(glob.glob(dirname))
...@@ -185,6 +212,7 @@ def compile_coffeescript(*files): ...@@ -185,6 +212,7 @@ def compile_coffeescript(*files):
@task @task
@no_help @no_help
@cmdopts([ @cmdopts([
('system=', 's', 'The system to compile sass for (defaults to all)'),
('debug', 'd', 'Debug mode'), ('debug', 'd', 'Debug mode'),
('force', '', 'Force full compilation'), ('force', '', 'Force full compilation'),
]) ])
...@@ -192,8 +220,18 @@ def compile_sass(options): ...@@ -192,8 +220,18 @@ def compile_sass(options):
""" """
Compile Sass to CSS. 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: if debug:
source_comments = True source_comments = True
output_style = 'nested' output_style = 'nested'
...@@ -202,22 +240,39 @@ def compile_sass(options): ...@@ -202,22 +240,39 @@ def compile_sass(options):
output_style = 'compressed' output_style = 'compressed'
timing_info = [] timing_info = []
system_sass_directories = applicable_sass_directories(systems)
for sass_dir in SASS_DIRS: all_sass_directories = applicable_sass_directories()
dry_run = tasks.environment.dry_run
for sass_dir in system_sass_directories:
start = datetime.now() start = datetime.now()
css_dir = sass_dir.parent / "css" css_dir = sass_dir.parent / "css"
sass.compile(
dirname=(sass_dir, css_dir), if force:
include_paths=SASS_LOAD_PATHS + SASS_DIRS, if dry_run:
source_comments=source_comments, tasks.environment.info("rm -rf {css_dir}/*.css".format(
output_style=output_style, css_dir=css_dir,
) ))
duration = datetime.now() - start else:
timing_info.append((sass_dir, css_dir, duration)) 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:") print("\t\tFinished compiling Sass:")
for sass_dir, css_dir, duration in timing_info: if not dry_run:
print(">> {} -> {} in {}s".format(sass_dir, css_dir, duration)) for sass_dir, css_dir, duration in timing_info:
print(">> {} -> {} in {}s".format(sass_dir, css_dir, duration))
def compile_templated_sass(systems, settings): def compile_templated_sass(systems, settings):
...@@ -226,15 +281,15 @@ 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) `systems` is a list of systems (e.g. 'lms' or 'studio' or both)
`settings` is the Django settings module to use. `settings` is the Django settings module to use.
""" """
for sys in systems: for system in systems:
if sys == "studio": if system == "studio":
sys = "cms" system = "cms"
sh(django_cmd( sh(django_cmd(
sys, settings, 'preprocess_assets', system, settings, 'preprocess_assets',
'{sys}/static/sass/*.scss'.format(sys=sys), '{system}/static/sass/*.scss'.format(system=system),
'{sys}/static/themed_sass'.format(sys=sys) '{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(): def process_xmodule_assets():
...@@ -310,7 +365,7 @@ def update_assets(args): ...@@ -310,7 +365,7 @@ def update_assets(args):
""" """
parser = argparse.ArgumentParser(prog='paver update_assets') parser = argparse.ArgumentParser(prog='paver update_assets')
parser.add_argument( parser.add_argument(
'system', type=str, nargs='*', default=['lms', 'studio'], 'system', type=str, nargs='*', default=ALL_SYSTEMS,
help="lms or studio", help="lms or studio",
) )
parser.add_argument( parser.add_argument(
...@@ -334,7 +389,7 @@ def update_assets(args): ...@@ -334,7 +389,7 @@ def update_assets(args):
compile_templated_sass(args.system, args.settings) compile_templated_sass(args.system, args.settings)
process_xmodule_assets() process_xmodule_assets()
compile_coffeescript() 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: if args.collect:
collect_assets(args.system, args.settings) 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 = ( ...@@ -11,21 +11,19 @@ EXPECTED_COFFEE_COMMAND = (
"{platform_root}/cms {platform_root}/common -type f -name \"*.coffee\"`" "{platform_root}/cms {platform_root}/common -type f -name \"*.coffee\"`"
) )
EXPECTED_SASS_COMMAND = ( EXPECTED_SASS_COMMAND = (
"sass --update --cache-location /tmp/sass-cache --default-encoding utf-8 --style compressed" "libsass {sass_directory}"
" --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"
) )
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 = ( EXPECTED_PREPROCESS_ASSETS_COMMAND = (
"python manage.py {system} --settings={asset_settings} preprocess_assets" "python manage.py {system} --settings={asset_settings} preprocess_assets"
" {system}/static/sass/*.scss {system}/static/themed_sass" " {system}/static/sass/*.scss {system}/static/themed_sass"
...@@ -236,7 +234,7 @@ class TestPaverServerTasks(PaverTestCase): ...@@ -236,7 +234,7 @@ class TestPaverServerTasks(PaverTestCase):
)) ))
expected_messages.append("xmodule_assets common/static/xmodule") expected_messages.append("xmodule_assets common/static/xmodule")
expected_messages.append(EXPECTED_COFFEE_COMMAND.format(platform_root=platform_root)) 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: if expected_collect_static:
expected_messages.append(EXPECTED_COLLECT_STATIC_COMMAND.format( expected_messages.append(EXPECTED_COLLECT_STATIC_COMMAND.format(
system=system, asset_settings=expected_asset_settings system=system, asset_settings=expected_asset_settings
...@@ -278,7 +276,7 @@ class TestPaverServerTasks(PaverTestCase): ...@@ -278,7 +276,7 @@ class TestPaverServerTasks(PaverTestCase):
)) ))
expected_messages.append("xmodule_assets common/static/xmodule") expected_messages.append("xmodule_assets common/static/xmodule")
expected_messages.append(EXPECTED_COFFEE_COMMAND.format(platform_root=platform_root)) 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: if expected_collect_static:
expected_messages.append(EXPECTED_COLLECT_STATIC_COMMAND.format( expected_messages.append(EXPECTED_COLLECT_STATIC_COMMAND.format(
system="lms", asset_settings=expected_asset_settings system="lms", asset_settings=expected_asset_settings
...@@ -302,3 +300,15 @@ class TestPaverServerTasks(PaverTestCase): ...@@ -302,3 +300,15 @@ class TestPaverServerTasks(PaverTestCase):
) )
expected_messages.append(EXPECTED_CELERY_COMMAND.format(settings="dev_with_worker")) expected_messages.append(EXPECTED_CELERY_COMMAND.format(settings="dev_with_worker"))
self.assertEquals(self.task_messages, expected_messages) 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 = [ ...@@ -24,6 +24,7 @@ PYTHON_REQ_FILES = [
'requirements/edx/github.txt', 'requirements/edx/github.txt',
'requirements/edx/local.txt', 'requirements/edx/local.txt',
'requirements/edx/base.txt', 'requirements/edx/base.txt',
'requirements/edx/paver.txt',
'requirements/edx/post.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