This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/attach-site-resources
in repository https://gitbox.apache.org/repos/asf/maven-site-plugin.git

commit 93a24bfa11145694bb93c470b9cc023f2cc510f8
Author: Konrad Windszus <k...@apache.org>
AuthorDate: Mon Dec 9 11:52:20 2024 +0100

    [MSITE-911] Add goal to archive and install/deploy site resources along
    with the project
    
    Consider those site-resource archives when setting up the site context
---
 pom.xml                                            |   6 +
 .../plugins/site/SiteResourcesAttachMojo.java      | 154 +++++++++++++++
 .../site/render/AbstractSiteRenderingMojo.java     |   8 +
 .../plugins/site/render/SiteResourcesResolver.java | 218 +++++++++++++++++++++
 4 files changed, 386 insertions(+)

diff --git a/pom.xml b/pom.xml
index 505bd596..5055a809 100644
--- a/pom.xml
+++ b/pom.xml
@@ -547,6 +547,12 @@ under the License.
         </plugin>
       </plugins>
     </pluginManagement>
+    <plugins>
+      <plugin>
+        <groupId>org.eclipse.sisu</groupId>
+        <artifactId>sisu-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
   </build>
 
   <profiles>
diff --git 
a/src/main/java/org/apache/maven/plugins/site/SiteResourcesAttachMojo.java 
b/src/main/java/org/apache/maven/plugins/site/SiteResourcesAttachMojo.java
new file mode 100644
index 00000000..6b6342c9
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/site/SiteResourcesAttachMojo.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+package org.apache.maven.plugins.site;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProjectHelper;
+import org.codehaus.plexus.archiver.Archiver;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.FileSet;
+import org.codehaus.plexus.archiver.util.DefaultFileSet;
+
+/**
+ * Adds the site resources (compressed in a ZIP archive) as dedicated artifact 
with classifier {@value #CLASSIFIER} to be installed/deployed.
+ * Usually used in combination with {@link SiteDescriptorAttachMojo} to also 
deploy the actual site descriptor.
+ * This is used for sites inheriting from this project
+ *
+ * @since next
+ */
+@Mojo(name = SiteResourcesAttachMojo.GOAL_NAME, defaultPhase = 
LifecyclePhase.PACKAGE, threadSafe = true)
+public class SiteResourcesAttachMojo extends AbstractSiteMojo {
+
+    public static final String GOAL_NAME = "attach-site-resources";
+
+    /**
+     * Directory containing the <code>site.xml</code> file and the source for 
hand written docs (one directory
+     * per Doxia-source-supported markup types)
+     * @see <a href="/doxia/references/index.html">Doxia Markup Languages 
References</a>.
+     */
+    @Parameter(defaultValue = "${basedir}/src/site")
+    protected File siteDirectory;
+
+    /**
+     * Maven ProjectHelper.
+     */
+    @Component
+    private MavenProjectHelper projectHelper;
+
+    @Component(hint = ARCHIVE_EXTENSION)
+    private Archiver zipArchiver;
+
+    /**
+     * The file name patterns to exclude (potentially in addition to the 
default ones mentioned at {@link #addDefaultExcludes}).
+     * The format of each pattern is described in {@link 
org.codehaus.plexus.util.DirectoryScanner}.
+     * The comparison is performed against the file path relative to the 
{@link #siteDirectory}.
+     * <p>
+     * Each value is either a regex pattern if enclosed within {@code %regex[} 
and {@code ]}, otherwise an
+     * <a href="https://ant.apache.org/manual/dirtasks.html#patterns";>Ant 
pattern</a>.
+     * Exclusions take precedence over inclusions via {@link #includes}.
+     */
+    @Parameter(defaultValue = "**/.gitignore,**/.gitattributes", required = 
true)
+    protected Set<String> excludes;
+
+    /**
+     * The file name patterns to include. The format of each pattern is 
described in {@link org.codehaus.plexus.util.DirectoryScanner}.
+     * The comparison is performed against the file path relative to the 
{@link #siteDirectory}.
+     * Since this is hardly predictable it is recommended to use only 
filename/directory name patterns here
+     * but not take into account file system hierarchies!
+     * <p>
+     * Each value is either a regex pattern if enclosed within {@code %regex[} 
and {@code ]}, otherwise an
+     * <a href="https://ant.apache.org/manual/dirtasks.html#patterns";>Ant 
pattern</a>.
+     * If this is not set, everything is included.
+     */
+    @Parameter(required = false)
+    protected Set<String> includes;
+
+    /**
+     * By default certain metadata files are excluded which means they will 
not be copied into the package.
+     * If you need them for a particular reason you can do that by setting 
this parameter to {@code false}.
+     *
+     * @see org.codehaus.plexus.util.AbstractScanner#DEFAULTEXCLUDES
+     */
+    @Parameter(defaultValue = "true")
+    protected boolean addDefaultExcludes;
+
+    /**
+     * Attach site resources in archive only if packaging is pom.
+     */
+    @Parameter(defaultValue = "true")
+    private boolean pomPackagingOnly;
+
+    public static final String CLASSIFIER = "site-resources";
+    public static final String ARCHIVE_EXTENSION = "zip";
+
+    public void execute() throws MojoExecutionException {
+        if (pomPackagingOnly && !"pom".equals(project.getPackaging())) {
+            // https://issues.apache.org/jira/browse/MSITE-597
+            getLog().info("Skipping because packaging '" + 
project.getPackaging() + "' is not pom.");
+            return;
+        }
+
+        if (siteDirectory.exists()) {
+            try {
+                File destFile = new File(
+                        project.getBuild().getDirectory(),
+                        project.getBuild().getFinalName() + CLASSIFIER + "." + 
ARCHIVE_EXTENSION);
+
+                File siteResourcesArchiveFile = createArchive(zipArchiver, 
destFile);
+                // Attach the site resources archive
+                getLog().info("Attaching site resources archive with 
classifier '" + CLASSIFIER + "'.");
+                projectHelper.attachArtifact(project, ARCHIVE_EXTENSION, 
CLASSIFIER, siteResourcesArchiveFile);
+            } catch (IOException e) {
+                throw new MojoExecutionException("Unable to archive site 
resources", e);
+            }
+        } else {
+            getLog().warn("No site resources found: nothing to attach.");
+        }
+    }
+
+    private FileSet createFileSet() {
+        DefaultFileSet fileSet = new DefaultFileSet();
+        fileSet.setDirectory(siteDirectory);
+        fileSet.setExcludes(excludes.toArray(new String[0]));
+        if (includes != null && !includes.isEmpty()) {
+            fileSet.setIncludes(includes.toArray(new String[0]));
+        }
+        fileSet.setUsingDefaultExcludes(addDefaultExcludes);
+        return fileSet;
+    }
+
+    public File createArchive(Archiver archiver, File destFile) throws 
ArchiverException, IOException {
+        archiver.setDestFile(destFile);
+        archiver.addFileSet(createFileSet());
+        archiver.createArchive();
+        return archiver.getDestFile();
+    }
+
+    public File getSiteDirectory() {
+        return siteDirectory;
+    }
+}
diff --git 
a/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java
 
b/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java
index 1030d63c..82202d4e 100644
--- 
a/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java
+++ 
b/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java
@@ -179,6 +179,12 @@ public abstract class AbstractSiteRenderingMojo extends 
AbstractSiteDescriptorMo
     @Component
     protected MavenReportExecutor mavenReportExecutor;
 
+    @Component
+    private MavenSession session;
+
+    @Component
+    protected SiteResourcesResolver siteResourcesResolver;
+
     /**
      * Gets the input files encoding.
      *
@@ -319,6 +325,8 @@ public abstract class AbstractSiteRenderingMojo extends 
AbstractSiteDescriptorMo
             context.addSiteDirectory(new SiteDirectory(generatedSiteDirectory, 
false));
         }
 
+        // potentially add inherited site resources
+        siteResourcesResolver.resolveParentSiteResources(session, project, 
generatedSiteDirectory, getLog());
         if (moduleExcludes != null) {
             context.setModuleExcludes(moduleExcludes);
         }
diff --git 
a/src/main/java/org/apache/maven/plugins/site/render/SiteResourcesResolver.java 
b/src/main/java/org/apache/maven/plugins/site/render/SiteResourcesResolver.java
new file mode 100644
index 00000000..f0f8ffaf
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/plugins/site/render/SiteResourcesResolver.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+package org.apache.maven.plugins.site.render;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.InvalidPluginDescriptorException;
+import org.apache.maven.plugin.MavenPluginManager;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoNotFoundException;
+import org.apache.maven.plugin.PluginConfigurationException;
+import org.apache.maven.plugin.PluginContainerException;
+import org.apache.maven.plugin.PluginDescriptorParsingException;
+import org.apache.maven.plugin.PluginResolutionException;
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.plugins.site.SiteResourcesAttachMojo;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.UnArchiver;
+import org.codehaus.plexus.archiver.dir.DirectoryArchiver;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+
+/**
+ * Resolves and extracts/copies inherited site resources from parent projects.
+ * @see SiteResourcesAttachMojo
+ */
+@Named
+@Singleton
+public class SiteResourcesResolver {
+
+    private final UnArchiver unArchiver;
+    private final RepositorySystem repoSystem;
+    private final MavenPluginManager pluginManager;
+
+    @Inject
+    public SiteResourcesResolver(
+            @Named(SiteResourcesAttachMojo.ARCHIVE_EXTENSION) UnArchiver 
unArchiver,
+            RepositorySystem repoSystem,
+            MavenPluginManager pluginManager) {
+        super();
+        this.unArchiver = unArchiver;
+        this.repoSystem = repoSystem;
+        this.pluginManager = pluginManager;
+    }
+
+    public void resolveParentSiteResources(
+            MavenSession mavenSession, MavenProject project, File 
generatedSiteDirectory, Log log) throws IOException {
+        MavenProject parent = project.getParent();
+        if (parent != null) {
+            // start with the topmost one to allow child projects to override 
parent resources
+            resolveParentSiteResources(mavenSession, parent, 
generatedSiteDirectory, log);
+            // is it part of a multi-module project?
+            if (parent.getBasedir() != null) {
+                resolveLocalSiteResources(mavenSession, parent, 
generatedSiteDirectory, log);
+            } else {
+                resolveRepositorySiteResources(
+                        RepositoryUtils.toArtifact(parent.getArtifact()),
+                        mavenSession.getRepositorySession(),
+                        parent.getRemoteProjectRepositories(),
+                        generatedSiteDirectory,
+                        log);
+            }
+        }
+    }
+
+    protected void resolveRepositorySiteResources(
+            Artifact artifact,
+            RepositorySystemSession repoSession,
+            List<RemoteRepository> remoteRepositories,
+            File generatedSiteDirectory,
+            Log log)
+            throws IOException {
+        final Artifact siteResourcesArtifact;
+        try {
+            siteResourcesArtifact = resolveSiteResources(artifact, 
repoSession, remoteRepositories);
+        } catch (ArtifactResolutionException e) {
+            throw new IOException("Error resolving site resources artifact for 
" + artifact, e);
+        }
+        if (siteResourcesArtifact != null) {
+            try {
+                extractArchive(siteResourcesArtifact.getFile(), 
generatedSiteDirectory);
+            } catch (ArchiverException e) {
+                throw new IOException("Error extracting archive " + 
siteResourcesArtifact.getFile(), e);
+            }
+            log.info("Copied inherited site resources from " + artifact + " to 
" + generatedSiteDirectory);
+        } else {
+            log.debug("No site resources found for " + artifact);
+        }
+    }
+
+    protected Artifact resolveSiteResources(
+            Artifact parentProjectArtifact,
+            RepositorySystemSession repoSession,
+            List<RemoteRepository> remoteRepositories)
+            throws ArtifactResolutionException {
+        try {
+            Artifact siteResourcesArtifact = new DefaultArtifact(
+                    parentProjectArtifact.getGroupId(),
+                    parentProjectArtifact.getArtifactId(),
+                    SiteResourcesAttachMojo.CLASSIFIER,
+                    SiteResourcesAttachMojo.ARCHIVE_EXTENSION,
+                    parentProjectArtifact.getVersion());
+            ArtifactRequest request =
+                    new ArtifactRequest(siteResourcesArtifact, 
remoteRepositories, "remote-site-resources");
+            ArtifactResult result = repoSystem.resolveArtifact(repoSession, 
request);
+            return result.getArtifact();
+        } catch (ArtifactResolutionException e) {
+            if (e.getCause() instanceof ArtifactNotFoundException) {
+                // this is no error
+                return null;
+            }
+            throw e;
+        }
+    }
+
+    protected void extractArchive(File archiveFile, File destDirectory) throws 
ArchiverException {
+        unArchiver.setSourceFile(archiveFile);
+        unArchiver.setDestDirectory(destDirectory);
+        unArchiver.setOverwrite(true);
+        unArchiver.extract();
+    }
+
+    private void resolveLocalSiteResources(
+            MavenSession mavenSession, MavenProject currentProject, File 
generatedSiteDirectory, Log log)
+            throws IOException {
+        // evaluate the configuration of the according mojo
+        Plugin sitePlugin = 
currentProject.getPlugin("org.apache.maven.plugins:maven-site-plugin");
+        if (sitePlugin != null) {
+            // just use the first potential execution's configuration
+            Optional<Xpp3Dom> configuration = 
sitePlugin.getExecutions().stream()
+                    .filter(e -> 
e.getGoals().contains("attach-site-resources"))
+                    .filter(e -> e.getPhase() != null && 
!e.getPhase().isEmpty())
+                    .map(e -> (Xpp3Dom) e.getConfiguration())
+                    .findFirst();
+            if (configuration.isPresent()) {
+                try {
+                    copySiteResourcesToDirectory(
+                            mavenSession, currentProject, sitePlugin, 
configuration.get(), generatedSiteDirectory, log);
+                } catch (ArchiverException e1) {
+                    throw new IOException("Error copying site resources to " + 
generatedSiteDirectory, e1);
+                } catch (MojoNotFoundException
+                        | PluginResolutionException
+                        | PluginDescriptorParsingException
+                        | InvalidPluginDescriptorException
+                        | PluginContainerException
+                        | PluginConfigurationException e1) {
+                    throw new IOException(
+                            "Error extracting mojo configuration from " + 
currentProject.getArtifact(), e1);
+                }
+            }
+        }
+    }
+
+    void copySiteResourcesToDirectory(
+            MavenSession mavenSession,
+            MavenProject project,
+            Plugin plugin,
+            Xpp3Dom configuration,
+            File destDirectory,
+            Log log)
+            throws ArchiverException, IOException, MojoNotFoundException, 
PluginResolutionException,
+                    PluginDescriptorParsingException, 
InvalidPluginDescriptorException, PluginContainerException,
+                    PluginConfigurationException {
+        // modify MavenSession to use the basedir of another project as context
+        MavenSession mavenSessionForOtherProject = mavenSession.clone();
+        mavenSessionForOtherProject.setCurrentProject(project);
+
+        MojoDescriptor mojoDescriptor = pluginManager.getMojoDescriptor(
+                plugin,
+                SiteResourcesAttachMojo.GOAL_NAME,
+                project.getRemotePluginRepositories(),
+                mavenSession.getRepositorySession());
+        MojoExecution mojoExecution = new MojoExecution(mojoDescriptor, 
configuration);
+        // TODO: use different baseDir for resolving default configuration
+        SiteResourcesAttachMojo mojo = pluginManager.getConfiguredMojo(
+                SiteResourcesAttachMojo.class, mavenSessionForOtherProject, 
mojoExecution);
+        // this is a pseudo archiver which just copies from source to 
destination considering the fileSet configuration
+        DirectoryArchiver archiver = new DirectoryArchiver();
+        mojo.createArchive(archiver, destDirectory);
+        log.info("Copied inherited site resources from " + 
mojo.getSiteDirectory() + " to " + destDirectory);
+    }
+}

Reply via email to