You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2021/09/15 11:49:41 UTC
[sling-scriptingbundle-maven-plugin] branch master updated:
SLING-10811 - Mark unresolved required capabilities as optional
This is an automated email from the ASF dual-hosted git repository.
radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-scriptingbundle-maven-plugin.git
The following commit(s) were added to refs/heads/master by this push:
new 1a23212 SLING-10811 - Mark unresolved required capabilities as optional
1a23212 is described below
commit 1a23212cab33a09bd8d3749f7ce159a02ce0e4a4
Author: Radu Cotescu <17...@users.noreply.github.com>
AuthorDate: Wed Sep 15 13:49:30 2021 +0200
SLING-10811 - Mark unresolved required capabilities as optional
* required capabilities which are not satisfied by the current project
are marked as optional; a flag can disable this behaviour, making these
required capabilities mandatory
---
.../plugin/bnd/BundledScriptsScannerPlugin.java | 12 ++++-
.../plugin/capability/Capabilities.java | 35 +++++++++----
.../capability/RequiredResourceTypeCapability.java | 2 +-
.../scriptingbundle/plugin/maven/MetadataMojo.java | 43 ++++-----------
.../plugin/processor/Constants.java | 1 +
src/site/markdown/usage.md.vm | 5 ++
.../scriptingbundle/plugin/AbstractPluginTest.java | 61 +++++++++-------------
src/test/resources/project-4/bnd.bnd | 18 +++++++
src/test/resources/project-4/pom.xml | 49 +++++++++++++++++
.../src/main/scripts/apps/components/test/requires | 1 +
.../main/scripts/apps/components/test/test.html | 18 +++++++
11 files changed, 161 insertions(+), 84 deletions(-)
diff --git a/src/main/java/org/apache/sling/scriptingbundle/plugin/bnd/BundledScriptsScannerPlugin.java b/src/main/java/org/apache/sling/scriptingbundle/plugin/bnd/BundledScriptsScannerPlugin.java
index e94d585..caa3293 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/plugin/bnd/BundledScriptsScannerPlugin.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/plugin/bnd/BundledScriptsScannerPlugin.java
@@ -93,7 +93,7 @@ public class BundledScriptsScannerPlugin implements AnalyzerPlugin, Plugin {
scriptEngineMappings = getConfiguredScriptEngineMappings();
capabilities = Capabilities
.fromFileSystemTree(workDirectory, walkPath(workDirectory, includes, excludes), logger,
- getConfiguredSearchPaths(), scriptEngineMappings);
+ getConfiguredSearchPaths(), scriptEngineMappings, getMissingRequirementsOptional());
String providedCapabilitiesDefinition = capabilities.getProvidedCapabilitiesString();
String requiredCapabilitiesDefinition = capabilities.getRequiredCapabilitiesString();
@@ -142,7 +142,6 @@ public class BundledScriptsScannerPlugin implements AnalyzerPlugin, Plugin {
}
private Set<PathMatcher> getConfiguredExcludes() {
-
String excludesCSV = pluginProperties.get(Constants.BND_EXCLUDES);
if (StringUtils.isNotEmpty(excludesCSV)) {
return Collections.unmodifiableSet(Arrays.stream(excludesCSV.split(",")).map(String::trim)
@@ -189,6 +188,15 @@ public class BundledScriptsScannerPlugin implements AnalyzerPlugin, Plugin {
return Constants.DEFAULT_SEARCH_PATHS;
}
+ private boolean getMissingRequirementsOptional() {
+ String missingRequirementsOptionalString = pluginProperties.get(Constants.BND_MISSING_REQUIREMENTS_OPTIONAL);
+ if (missingRequirementsOptionalString != null) {
+ missingRequirementsOptionalString = missingRequirementsOptionalString.trim().toLowerCase();
+ return !"false".equals(missingRequirementsOptionalString);
+ }
+ return true;
+ }
+
private Stream<Path> walkPath(Path path, Set<PathMatcher> includes, Set<PathMatcher> excludes) throws IOException {
return Files.walk(path).filter(file -> {
boolean include = false;
diff --git a/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/Capabilities.java b/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/Capabilities.java
index d8fc5cb..bd8e4de 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/Capabilities.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/Capabilities.java
@@ -21,7 +21,6 @@ package org.apache.sling.scriptingbundle.plugin.capability;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
@@ -45,7 +44,6 @@ public class Capabilities {
private final Set<ProvidedResourceTypeCapability> providedResourceTypeCapabilities;
private final Set<ProvidedScriptCapability> providedScriptCapabilities;
private final Set<RequiredResourceTypeCapability> requiredResourceTypeCapabilities;
- private final Set<RequiredResourceTypeCapability> unresolvedRequiredResourceTypeCapabilities;
public static final Capabilities EMPTY = new Capabilities(Collections.emptySet(), Collections.emptySet(), Collections.emptySet());
public Capabilities(
@@ -55,9 +53,6 @@ public class Capabilities {
this.providedResourceTypeCapabilities = providedResourceTypeCapabilities;
this.providedScriptCapabilities = providedScriptCapabilities;
this.requiredResourceTypeCapabilities = requiredResourceTypeCapabilities;
- unresolvedRequiredResourceTypeCapabilities = new HashSet<>(requiredResourceTypeCapabilities);
- providedResourceTypeCapabilities.forEach(providedResourceTypeCapability -> unresolvedRequiredResourceTypeCapabilities
- .removeIf(requiredResourceTypeCapability -> requiredResourceTypeCapability.isSatisfied(providedResourceTypeCapability)));
}
public @NotNull Set<ProvidedResourceTypeCapability> getProvidedResourceTypeCapabilities() {
@@ -72,10 +67,6 @@ public class Capabilities {
return Collections.unmodifiableSet(requiredResourceTypeCapabilities);
}
- public @NotNull Set<RequiredResourceTypeCapability> getUnresolvedRequiredResourceTypeCapabilities() {
- return Collections.unmodifiableSet(unresolvedRequiredResourceTypeCapabilities);
- }
-
public @NotNull String getProvidedCapabilitiesString() {
Parameters parameters = new Parameters();
for (ProvidedResourceTypeCapability capability : getProvidedResourceTypeCapabilities()) {
@@ -139,7 +130,9 @@ public class Capabilities {
}
public static @NotNull Capabilities fromFileSystemTree(@NotNull Path root, @NotNull Stream<Path> files, @NotNull Logger logger,
- @NotNull Set<String> searchPaths, @NotNull Map<String, String> scriptEngineMappings) {
+ @NotNull Set<String> searchPaths,
+ @NotNull Map<String, String> scriptEngineMappings,
+ boolean missingRequirementsOptional) {
Set<ProvidedResourceTypeCapability> providedResourceTypeCapabilities = new LinkedHashSet<>();
Set<ProvidedScriptCapability> providedScriptCapabilities = new LinkedHashSet<>();
Set<RequiredResourceTypeCapability> requiredResourceTypeCapabilities = new LinkedHashSet<>();
@@ -158,7 +151,27 @@ public class Capabilities {
requiredResourceTypeCapabilities.addAll(pathCapabilities.getRequiredResourceTypeCapabilities());
}
});
- return new Capabilities(providedResourceTypeCapabilities, providedScriptCapabilities, requiredResourceTypeCapabilities);
+ final Set<RequiredResourceTypeCapability> required = new LinkedHashSet<>();
+ if (missingRequirementsOptional) {
+ Set<RequiredResourceTypeCapability> unresolvedRequiredResourceTypeCapabilities =
+ new LinkedHashSet<>(requiredResourceTypeCapabilities);
+ providedResourceTypeCapabilities.forEach(providedResourceTypeCapability -> unresolvedRequiredResourceTypeCapabilities
+ .removeIf(
+ requiredResourceTypeCapability -> requiredResourceTypeCapability.isSatisfied(providedResourceTypeCapability)));
+
+ requiredResourceTypeCapabilities.forEach(requiredResourceTypeCapability -> {
+ if (unresolvedRequiredResourceTypeCapabilities.contains(requiredResourceTypeCapability)) {
+ required.add(
+ RequiredResourceTypeCapability.builder().withResourceType(requiredResourceTypeCapability.getResourceType())
+ .withVersionRange(requiredResourceTypeCapability.getVersionRange()).withIsOptional().build());
+ } else {
+ required.add(requiredResourceTypeCapability);
+ }
+ });
+ } else {
+ required.addAll(requiredResourceTypeCapabilities);
+ }
+ return new Capabilities(providedResourceTypeCapabilities, providedScriptCapabilities, required);
}
}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/RequiredResourceTypeCapability.java b/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/RequiredResourceTypeCapability.java
index 7fbab3b..b7a2df2 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/RequiredResourceTypeCapability.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/plugin/capability/RequiredResourceTypeCapability.java
@@ -78,7 +78,7 @@ public class RequiredResourceTypeCapability {
@Override
public String toString() {
- return String.format("%s{resourceType=%s, versionRange=%s, isOptonal=%s}", this.getClass().getSimpleName(),
+ return String.format("%s{resourceType=%s, versionRange=%s, isOptional=%s}", this.getClass().getSimpleName(),
resourceType, versionRange, isOptional);
}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/plugin/maven/MetadataMojo.java b/src/main/java/org/apache/sling/scriptingbundle/plugin/maven/MetadataMojo.java
index f72eaad..32d0f66 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/plugin/maven/MetadataMojo.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/plugin/maven/MetadataMojo.java
@@ -41,14 +41,10 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.utils.io.DirectoryScanner;
-import org.apache.sling.api.servlets.ServletResolverConstants;
import org.apache.sling.scriptingbundle.plugin.capability.Capabilities;
-import org.apache.sling.scriptingbundle.plugin.capability.RequiredResourceTypeCapability;
import org.apache.sling.scriptingbundle.plugin.processor.Constants;
import org.apache.sling.scriptingbundle.plugin.processor.Logger;
import org.jetbrains.annotations.NotNull;
-import org.osgi.framework.Version;
-import org.osgi.framework.VersionRange;
/**
* The {@code metadata} goal will generate two Maven project properties, namely
@@ -171,6 +167,14 @@ public class MetadataMojo extends AbstractMojo {
@Parameter(property = "scriptingbundle.searchPaths")
private Set<String> searchPaths;
+ /**
+ * When set to "true", the requirements which are not satisfied directly by this project will be marked as optional.
+ *
+ * @since 0.5.0
+ */
+ @Parameter(property = "scriptingbundle.missingRequirementsOptional", defaultValue = "true")
+ private boolean missingRequirementsOptional = true;
+
private Capabilities capabilities;
public void execute() {
@@ -221,18 +225,15 @@ public class MetadataMojo extends AbstractMojo {
scannerPaths.stream().map(workDirectory::resolve),
logger,
searchPaths,
- scriptEngineMappings
+ scriptEngineMappings,
+ missingRequirementsOptional
);
String providedCapabilitiesDefinition = capabilities.getProvidedCapabilitiesString();
String requiredCapabilitiesDefinition = capabilities.getRequiredCapabilitiesString();
- String unresolvedRequiredCapabilitiesDefinition = getUnresolvedRequiredCapabilitiesString(capabilities);
project.getProperties().put("org.apache.sling.scriptingbundle.maven.plugin." + org.osgi.framework.Constants.PROVIDE_CAPABILITY,
providedCapabilitiesDefinition);
project.getProperties().put("org.apache.sling.scriptingbundle.maven.plugin." + org.osgi.framework.Constants.REQUIRE_CAPABILITY,
requiredCapabilitiesDefinition);
- project.getProperties()
- .put("org.apache.sling.scriptingbundle.maven.plugin.Unresolved-" + org.osgi.framework.Constants.REQUIRE_CAPABILITY,
- unresolvedRequiredCapabilitiesDefinition);
} catch (IOException e) {
logger.error("Unable to generate working directory.", e);
}
@@ -256,30 +257,6 @@ public class MetadataMojo extends AbstractMojo {
return scanner;
}
- @NotNull
- private String getUnresolvedRequiredCapabilitiesString(Capabilities capabilities) {
- StringBuilder builder = new StringBuilder();
- int pcNum = capabilities.getUnresolvedRequiredResourceTypeCapabilities().size();
- int pcIndex = 0;
- for (RequiredResourceTypeCapability capability : capabilities.getUnresolvedRequiredResourceTypeCapabilities()) {
- builder.append(Constants.CAPABILITY_NS).append(";");
- builder.append(ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES).append("=").append(capability.getResourceType());
- VersionRange versionRange = capability.getVersionRange();
- if (versionRange != null) {
- Version left = versionRange.getLeft();
- if (versionRange.getLeftType() == VersionRange.LEFT_OPEN) {
- left = new Version(left.getMajor(), left.getMinor(), left.getMicro() + 1);
- }
- builder.append(";").append(Constants.CAPABILITY_VERSION_AT).append("=").append(left);
- }
- if (pcIndex < pcNum - 1) {
- builder.append(",");
- }
- pcIndex++;
- }
- return builder.toString();
- }
-
Capabilities getCapabilities() {
return capabilities;
}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/Constants.java b/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/Constants.java
index c9da665..55cbc89 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/Constants.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/Constants.java
@@ -52,6 +52,7 @@ public final class Constants {
public static final String BND_INCLUDES = "includes";
public static final String BND_SCRIPT_ENGINE_MAPPINGS = "scriptEngineMappings";
public static final String BND_SEARCH_PATHS = "searchPaths";
+ public static final String BND_MISSING_REQUIREMENTS_OPTIONAL = "missingRequirementsOptional";
public static final Map<String, String> DEFAULT_EXTENSION_TO_SCRIPT_ENGINE_MAPPING;
public static final Set<String> DEFAULT_SEARCH_PATHS;
diff --git a/src/site/markdown/usage.md.vm b/src/site/markdown/usage.md.vm
index 626e289..b1bec3e 100644
--- a/src/site/markdown/usage.md.vm
+++ b/src/site/markdown/usage.md.vm
@@ -36,6 +36,11 @@ page. In addition to the normal way of structuring scripts in the file tree, the
is defined as a package name, the resource type label will be the last subpackage (i.e. for `com.mydomain.components.image`, the
resource type label will be `image`).
+Starting with version 0.5.0, the plugin will mark the requirements which are not satisfied by the analysed project as optional; classpath
+dependencies are not checked for provided capabilities. If you want to generate mandatory requirements, set the `missingRequirementsOptional`
+flag to `false`.
+
+
$h3 Defining scripts
As an example, let's assume the following layout:
```
diff --git a/src/test/java/org/apache/sling/scriptingbundle/plugin/AbstractPluginTest.java b/src/test/java/org/apache/sling/scriptingbundle/plugin/AbstractPluginTest.java
index fa35391..9a96b9b 100644
--- a/src/test/java/org/apache/sling/scriptingbundle/plugin/AbstractPluginTest.java
+++ b/src/test/java/org/apache/sling/scriptingbundle/plugin/AbstractPluginTest.java
@@ -135,7 +135,7 @@ public abstract class AbstractPluginTest {
Set<RequiredResourceTypeCapability> rExpected = new HashSet<>(Arrays.asList(
RequiredResourceTypeCapability.builder().withResourceType("sling/default")
- .withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).build(),
+ .withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).withIsOptional().build(),
RequiredResourceTypeCapability.builder().withResourceType("org/apache/sling/bar").build(),
RequiredResourceTypeCapability.builder().withResourceType("org/apache/sling/bar")
.withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).withIsOptional().build()
@@ -144,11 +144,7 @@ public abstract class AbstractPluginTest {
ProvidedScriptCapability.builder(scriptEngineMappings)
.withPath("/org.apache.sling.wrongbar/wrongbar.has.too.many.selectors.html").build()
));
- Set<RequiredResourceTypeCapability> urExpected = new HashSet<>(Arrays.asList(
- RequiredResourceTypeCapability.builder().withResourceType("sling/default")
- .withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).build()
- ));
- verifyCapabilities(capabilities, pExpected, rExpected, sExpected, urExpected);
+ verifyCapabilities(capabilities, pExpected, rExpected, sExpected);
} finally {
cleanUp("project-1");
}
@@ -174,13 +170,9 @@ public abstract class AbstractPluginTest {
));
Set<RequiredResourceTypeCapability> expectedRequired = new HashSet<>(Arrays.asList(
RequiredResourceTypeCapability.builder().withResourceType("sling/scripting/warpDrive")
- .withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).build()
- ));
- Set<RequiredResourceTypeCapability> expectedUnresolvedRequired = new HashSet<>(Arrays.asList(
- RequiredResourceTypeCapability.builder().withResourceType("sling/scripting/warpDrive")
- .withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).build()
+ .withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).withIsOptional().build()
));
- verifyCapabilities(capabilities, pExpected, expectedRequired, expectedScriptCapabilities, expectedUnresolvedRequired);
+ verifyCapabilities(capabilities, pExpected, expectedRequired, expectedScriptCapabilities);
} finally {
cleanUp("project-2");
}
@@ -198,13 +190,31 @@ public abstract class AbstractPluginTest {
.withSelectors(Arrays.asList("selector")).withScriptEngine("htl").withScriptExtension("html")
.build()
));
- verifyCapabilities(capabilities, pExpected, Collections.emptySet(), Collections.emptySet(), Collections.emptySet());
+ verifyCapabilities(capabilities, pExpected, Collections.emptySet(), Collections.emptySet());
} finally {
cleanUp("project-3");
}
}
- private void verifyCapabilities(Capabilities capabilities, Set<ProvidedResourceTypeCapability> pExpected, Set<RequiredResourceTypeCapability> rExpected, Set<ProvidedScriptCapability> sExpected, Set<RequiredResourceTypeCapability> urExpected) {
+ @Test
+ public void testProject4() throws Exception {
+ try {
+ PluginExecution execution = executePluginOnProject("project-4");
+ Capabilities capabilities = execution.getCapabilities();
+ Set<ProvidedResourceTypeCapability> pExpected = new HashSet<>(Arrays.asList(
+ ProvidedResourceTypeCapability.builder().withResourceTypes("components/test", "/apps/components/test")
+ .withScriptEngine("htl").withScriptExtension("html").build()
+ ));
+ Set<RequiredResourceTypeCapability> rExpected = new HashSet<>(Arrays.asList(
+ RequiredResourceTypeCapability.builder().withResourceType("components/testhelper").build()
+ ));
+ verifyCapabilities(capabilities, pExpected, rExpected, Collections.emptySet());
+ } finally {
+ cleanUp("project-4");
+ }
+ }
+
+ private void verifyCapabilities(Capabilities capabilities, Set<ProvidedResourceTypeCapability> pExpected, Set<RequiredResourceTypeCapability> rExpected, Set<ProvidedScriptCapability> sExpected) {
Set<ProvidedResourceTypeCapability> provided = new HashSet<>(capabilities.getProvidedResourceTypeCapabilities());
StringBuilder missingProvided = new StringBuilder();
for (ProvidedResourceTypeCapability capability : pExpected) {
@@ -263,28 +273,5 @@ public abstract class AbstractPluginTest {
if (extraProvidedScripts.length() > 0) {
fail(extraProvidedScripts.toString());
}
-
- Set<RequiredResourceTypeCapability> unresolvedRequired =
- new HashSet<>(capabilities.getUnresolvedRequiredResourceTypeCapabilities());
- assertEquals(urExpected.size(), unresolvedRequired.size());
- StringBuilder missingUnresolvedRequired = new StringBuilder();
- for (RequiredResourceTypeCapability capability : urExpected) {
- boolean removed = unresolvedRequired.remove(capability);
- if (!removed) {
- missingUnresolvedRequired.append("Missing unresolved required capability: ").append(capability.toString())
- .append(System.lineSeparator());
- }
- }
- if (missingUnresolvedRequired.length() > 0) {
- fail(missingUnresolvedRequired.toString());
- }
- StringBuilder extraUnresolvedRequired = new StringBuilder();
- for (RequiredResourceTypeCapability capability : unresolvedRequired) {
- extraUnresolvedRequired.append("Extra unresolved required capability: ").append(capability.toString())
- .append(System.lineSeparator());
- }
- if (extraUnresolvedRequired.length() > 0) {
- fail(extraUnresolvedRequired.toString());
- }
}
}
diff --git a/src/test/resources/project-4/bnd.bnd b/src/test/resources/project-4/bnd.bnd
new file mode 100644
index 0000000..173468c
--- /dev/null
+++ b/src/test/resources/project-4/bnd.bnd
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+-plugin: org.apache.sling.scriptingbundle.plugin.bnd.BundledScriptsScannerPlugin; \
+ missingRequirementsOptional=false
diff --git a/src/test/resources/project-4/pom.xml b/src/test/resources/project-4/pom.xml
new file mode 100644
index 0000000..4a871d8
--- /dev/null
+++ b/src/test/resources/project-4/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.sling</groupId>
+ <artifactId>scriptingbundle-maven-plugin-project-1</artifactId>
+ <version>0.0.1</version>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>scriptingbundle-maven-plugin</artifactId>
+ <configuration>
+ <missingRequirementsOptional>false</missingRequirementsOptional>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>metadata</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/src/test/resources/project-4/src/main/scripts/apps/components/test/requires b/src/test/resources/project-4/src/main/scripts/apps/components/test/requires
new file mode 100644
index 0000000..3569a01
--- /dev/null
+++ b/src/test/resources/project-4/src/main/scripts/apps/components/test/requires
@@ -0,0 +1 @@
+components/testhelper
diff --git a/src/test/resources/project-4/src/main/scripts/apps/components/test/test.html b/src/test/resources/project-4/src/main/scripts/apps/components/test/test.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-4/src/main/scripts/apps/components/test/test.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->