Repository: plone/Products.CMFPlone

Issue:
plone-compile-resources (_generate_gruntfile.py) fails on (any) resourceDirectory using browser layer
`_generate_gruntfile.py` uses `Traversable.unrestrictedTraverse()` to introspect resources provided by browser resources and/or resource directories.  This fails for any resourceDirectory where plone.browserlayer layer specification is used, which pits core resource compilation process against a stated best-practice for add-on developers.  This is even the case when the bundle being compiled is completely unrelated to the add-on in question.

**This should never happen.**  This failure can be fixed by having the script load all declared layers.

### Reproducing:

In Products.CMFPlone master (~5.1b4.dev0), add any add-on with:

1. a browser:resourceDirectory declaration in ZCML, with a layer parameter to specify a browser layer interface (plone.browserlayer);
2. registry.xml entries for resources and/or bundles using those resources;
3. Run ./bin/plone-compile-resources for your instance.

### Desired outcome:

_Successful build, no errors._

### What actually happened:

**TraversalError [attempting to traverse to the namespace/name in question](https://community.plone.org/t/plone-compile-resources-fails-for-resourcedirectory-using-layer/4393), because lookup using acquired request fails when layers are not marked on the request.**

### What version of Plone/ Addons I am using:

Products.CMFPlone master (~5.1b4.dev0).

### Fixing:

**Layers should be loaded by script; there is an available request object to mark.**  This is trivially implemented by getting registrations from the site's components, and marking the request with all interfaces.



Discussion:

Resolving patch:
diff --git a/Products/CMFPlone/_scripts/_generate_gruntfile.py b/Products/CMFPlone/_scripts/_generate_gruntfile.py
--- a/Products/CMFPlone/_scripts/_generate_gruntfile.py
+++ b/Products/CMFPlone/_scripts/_generate_gruntfile.py
@@ -1,4 +1,6 @@
 # -*- coding: utf-8 -*-
+from Acquisition import aq_acquire
+from plone.browserlayer.interfaces import ILocalBrowserLayerType
 from plone.registry.interfaces import IRegistry
 from plone.resource.directory import FilesystemResourceDirectory
 from plone.resource.file import FilesystemFile
@@ -10,6 +12,8 @@
 from Products.Five.browser.resource import DirectoryResource
 from Products.Five.browser.resource import FileResource
 from zope.component import getUtility
+from zope.component import getSiteManager
+from zope.interface import alsoProvides
 from zope.site.hooks import setSite
 
 import json
@@ -26,6 +30,15 @@
     HAS_BARCELONETA = True
     import plonetheme.barceloneta
 
+
+def applyBrowserLayers(site):
+    request = aq_acquire(site, 'REQUEST')
+    sm = getSiteManager(site)
+    layers = sm.getAllUtilitiesRegisteredFor(ILocalBrowserLayerType)
+    for layer in layers:
+        alsoProvides(request, layer)
+
+
 # some initial script setup
 if 'SITE_ID' in os.environ:
     site_id = os.environ['SITE_ID']
@@ -44,6 +57,8 @@
     current = current[part]
 portal = current
 setSite(portal)
+applyBrowserLayers(portal)
+
 
 # start the juicy stuff
 temp_resource_folder = 'temp_resources'


Test patch: