Skip to content

Commit

Permalink
Support compiling SASS for only one system
Browse files Browse the repository at this point in the history
  • Loading branch information
andy-armstrong committed Jan 29, 2016
1 parent c13e623 commit 99c90a6
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 51 deletions.
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
source 'https://rubygems.org'
gem 'sass', '3.3.5'
gem 'bourbon', '~> 4.0.2'
gem 'neat', '~> 1.6.0'
3 changes: 1 addition & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,4 +16,3 @@ PLATFORMS
DEPENDENCIES
bourbon (~> 4.0.2)
neat (~> 1.6.0)
sass (= 3.3.5)
1 change: 1 addition & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pavelib/acceptance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
])
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),
Expand Down
117 changes: 86 additions & 31 deletions pavelib/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,28 @@
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():
Expand All @@ -44,24 +48,47 @@ 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"])
lms_sass = theme_dir / "lms" / "static" / "sass"
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
Expand Down Expand Up @@ -99,7 +126,7 @@ def register(self, observer):
"""
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))
Expand Down Expand Up @@ -185,15 +212,26 @@ 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'),
])
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'
Expand All @@ -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):
Expand All @@ -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():
Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand Down
58 changes: 58 additions & 0 deletions pavelib/paver_tests/test_assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""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)
42 changes: 26 additions & 16 deletions pavelib/paver_tests/test_servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,19 @@
"{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"
Expand Down Expand Up @@ -236,7 +234,7 @@ def verify_server_task(self, task_name, options, contracts_default=False):
))
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
Expand Down Expand Up @@ -278,7 +276,7 @@ def verify_run_all_servers_task(self, options):
))
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
Expand All @@ -302,3 +300,15 @@ def verify_run_all_servers_task(self, options):
)
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]
1 change: 1 addition & 0 deletions pavelib/prereqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
'requirements/edx/github.txt',
'requirements/edx/local.txt',
'requirements/edx/base.txt',
'requirements/edx/paver.txt',
'requirements/edx/post.txt',
]

Expand Down

0 comments on commit 99c90a6

Please sign in to comment.