Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to configure trailing slash redirect behavior #66

Merged
merged 7 commits into from Apr 10, 2015
6 changes: 3 additions & 3 deletions grow/commands/build.py
Expand Up @@ -17,11 +17,11 @@ def build(pod_path, out_dir):
pod = pods.Pod(root, storage=storage.FileStorage)
pod.preprocess()
try:
paths_to_contents = pod.dump()
repo = utils.get_git_repo(pod.root)
config = local_destination.Config(out_dir=out_dir)
stats_obj = stats.Stats(pod, paths_to_contents=paths_to_contents)
destination = local_destination.LocalDestination(config)
paths_to_contents = destination.dump(pod)
repo = utils.get_git_repo(pod.root)
stats_obj = stats.Stats(pod, paths_to_contents=paths_to_contents)
destination.deploy(paths_to_contents, stats=stats_obj, repo=repo, confirm=False,
test=False)
except pods.Error as e:
Expand Down
4 changes: 1 addition & 3 deletions grow/commands/deploy.py
Expand Up @@ -30,12 +30,10 @@ def deploy(deployment_name, pod_path, build, confirm, test, test_only, auth):
deployment.login(auth)
if build:
pod.preprocess()
# Set the environment information for the pod based on the deployment.
pod.env = deployment.get_env()
if test_only:
deployment.test()
return
paths_to_contents = pod.dump()
paths_to_contents = deployment.dump(pod)
repo = utils.get_git_repo(pod.root)
stats_obj = stats.Stats(pod, paths_to_contents=paths_to_contents)
deployment.deploy(paths_to_contents, stats=stats_obj, repo=repo,
Expand Down
24 changes: 19 additions & 5 deletions grow/deployments/destinations/amazon_s3.py
Expand Up @@ -17,6 +17,10 @@ class Config(messages.Message):
access_secret = messages.StringField(3)
env = messages.MessageField(env.EnvConfig, 4)
keep_control_dir = messages.BooleanField(5, default=False)
redirect_trailing_slashes = messages.BooleanField(6, default=True)
index_document = messages.StringField(7, default='index.html')
error_document = messages.StringField(8, default='404.html')



class AmazonS3Destination(base.BaseDestination):
Expand All @@ -33,13 +37,21 @@ def bucket(self):
calling_format=connection.OrdinaryCallingFormat())
return boto_connection.get_bucket(self.config.bucket)

def dump(self, pod):
pod.env = self.get_env()
return pod.dump(
suffix=self.config.index_document,
append_slashes=self.config.redirect_trailing_slashes)

def prelaunch(self, dry_run=False):
if dry_run:
return
logging.info('Configuring S3 bucket: {}'.format(self.config.bucket))
self.bucket.set_acl('public-read')
self.bucket.configure_versioning(False)
self.bucket.configure_website('index.html', '404.html')
self.bucket.configure_website(
self.config.index_document,
self.config.error_document)

def write_control_file(self, path, content):
path = os.path.join(self.control_dir, path.lstrip('/'))
Expand All @@ -62,17 +74,19 @@ def delete_file(self, path):

def write_file(self, path, content, policy='public-read'):
path = path.lstrip('/')
path = path if path != '' else self.config.index_document
if isinstance(content, unicode):
content = content.encode('utf-8')
bucket_key = key.Key(self.bucket)
bucket_key.key = path
fp = cStringIO.StringIO()
fp.write(content)
# TODO(jeremydw): Better headers.
mimetype = mimetypes.guess_type(path)[0]
headers = {'Cache-Control': 'no-cache'}
if mimetype:
headers['Content-Type'] = mimetype
# TODO: Allow configurable headers.
headers = {
'Cache-Control': 'no-cache',
'Content-Type': mimetype if mimetype else 'text/html',
}
fp.seek(0)
bucket_key.set_contents_from_file(fp, headers=headers, replace=True, policy=policy)
fp.close()
4 changes: 4 additions & 0 deletions grow/deployments/destinations/base.py
Expand Up @@ -200,6 +200,10 @@ def prelaunch(self, dry_run=False):
def login(self, account, reauth=False):
pass

def dump(self, pod):
pod.env = self.get_env()
return pod.dump()

def deploy(self, paths_to_contents, stats=None, repo=None, dry_run=False, confirm=False,
test=True):
self.prelaunch(dry_run=dry_run)
Expand Down
31 changes: 23 additions & 8 deletions grow/deployments/destinations/google_cloud_storage.py
Expand Up @@ -55,6 +55,9 @@ class Config(messages.Message):
key_path = messages.StringField(6)
env = messages.MessageField(env.EnvConfig, 7)
keep_control_dir = messages.BooleanField(8, default=False)
redirect_trailing_slashes = messages.BooleanField(9, default=True)
main_page_suffix = messages.StringField(10, default='index.html')
not_found_page = messages.StringField(11, default='404.html')


class GoogleCloudStorageDestination(base.BaseDestination):
Expand Down Expand Up @@ -84,19 +87,29 @@ def bucket(self):
self.config.project, self.config.email, self.config.key_path)
return gs_connection.get_bucket(self.config.bucket)

def dump(self, pod):
pod.env = self.get_env()
return pod.dump(
suffix=self.config.main_page_suffix,
append_slashes=self.config.redirect_trailing_slashes)

def prelaunch(self, dry_run=False):
if dry_run:
return
logging.info('Configuring GS bucket: {}'.format(self.config.bucket))
logging.info('Configuring GCS bucket: {}'.format(self.config.bucket))
if self.use_interoperable_auth:
self.bucket.set_acl('public-read')
self.bucket.configure_versioning(False)
self.bucket.configure_website(main_page_suffix='index.html', error_key='404.html')
self.bucket.configure_website(
main_page_suffix=self.config.main_page_suffix,
error_key=self.config.not_found_page)
else:
acl = self.bucket.get_default_object_acl()
acl.all().grant_read().revoke_write()
acl.save()
self.bucket.configure_website(main_page_suffix='index.html', not_found_page='404.html')
self.bucket.configure_website(
main_page_suffix=self.config.main_page_suffix,
not_found_page=self.config.not_found_page)

def write_control_file(self, path, content):
path = os.path.join(self.control_dir, path.lstrip('/'))
Expand Down Expand Up @@ -134,18 +147,20 @@ def write_file(self, path, content, policy='public-read'):
if isinstance(content, unicode):
content = content.encode('utf-8')
path = path.lstrip('/')
mimetype = mimetypes.guess_type(path)[0]
path = path if path != '' else self.config.main_page_suffix
mimetype = mimetypes.guess_type(path)[0] or 'text/html'
fp = cStringIO.StringIO()
fp.write(content)
size = fp.tell()

try:
if self.use_interoperable_auth:
file_key = key.Key(self.bucket)
file_key.key = path
headers = {'Cache-Control': 'no-cache'} # TODO(jeremydw): Better headers.
if mimetype:
headers['Content-Type'] = mimetype
# TODO: Allow configurable headers.
headers = {
'Cache-Control': 'no-cache',
'Content-Type': mimetype,
}
file_key.set_contents_from_file(fp, headers=headers, replace=True, policy=policy,
size=size, rewind=True)
else:
Expand Down
8 changes: 4 additions & 4 deletions grow/pods/controllers/tags/builtins.py
Expand Up @@ -68,10 +68,6 @@ def static(path, _pod=None):
return path


def get_doc(pod_path, locale=None, _pod=None):
return _pod.routes.get_doc(pod_path, locale=locale)


class Menu(object):

def __init__(self):
Expand Down Expand Up @@ -107,6 +103,10 @@ def url(pod_path, locale=None, _pod=None):
return doc.url


def get_doc(pod_path, locale=None, _pod=None):
return _pod.get_doc(pod_path, locale=locale)


@jinja2.contextfilter
def render_filter(ctx, template):
if isinstance(template, basestring):
Expand Down
10 changes: 7 additions & 3 deletions grow/pods/pods.py
Expand Up @@ -227,13 +227,17 @@ def export(self):
output['/404.html'] = error_controller.render()
return output

def dump(self, suffix='index.html'):
def dump(self, suffix='index.html', append_slashes=True):
output = self.export()
clean_output = {}
if suffix:
for path, content in output.iteritems():
if suffix and path.endswith('/') or '.' not in os.path.basename(path):
path = path.rstrip('/') + '/' + suffix
if (append_slashes
and not path.endswith('/')
and not os.path.splitext(path)[-1]):
path = path.rstrip('/') + '/'
if append_slashes and path.endswith('/') and suffix:
path += suffix
clean_output[path] = content
else:
clean_output = output
Expand Down
32 changes: 31 additions & 1 deletion grow/pods/pods_test.py
Expand Up @@ -38,7 +38,37 @@ def test_export(self):
self.pod.export()

def test_dump(self):
self.pod.dump()
paths = [
'/about/index.html',
'/contact-us/index.html',
'/de/about/index.html',
'/de/contact-us/index.html',
'/de/home/index.html',
'/de/html/index.html',
'/de/intro/index.html',
'/fr/about/index.html',
'/fr/contact-us/index.html',
'/fr/home/index.html',
'/fr/html/index.html',
'/fr/intro/index.html',
'/html/index.html',
'/index.html',
'/intro/index.html',
'/it/about/index.html',
'/it/contact-us/index.html',
'/it/home/index.html',
'/it/html/index.html',
'/it/intro/index.html',
'/post/newer/index.html',
'/post/newest/index.html',
'/post/older/index.html',
'/post/oldest/index.html',
'/public/file.txt',
'/public/main.css',
'/public/main.min.js',
]
result = self.pod.dump()
self.assertItemsEqual(paths, result)

def test_to_message(self):
self.pod.to_message()
Expand Down