All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atlassian.bamboo.specs.maven.RunBambooSpecsMojo Maven / Gradle / Ivy

There is a newer version: 10.1.0
Show newest version
package com.atlassian.bamboo.specs.maven;

import com.atlassian.bamboo.specs.api.rsbs.RepositoryStoredSpecsData;
import com.atlassian.bamboo.specs.api.rsbs.RunnerSettings;
import com.atlassian.bamboo.specs.maven.sandbox.SpecsRunner;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Execute;
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.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Mojo(
        name = "run",
        defaultPhase = LifecyclePhase.NONE,
        requiresDependencyResolution = ResolutionScope.RUNTIME,
        requiresOnline = true

)
@Execute(phase = LifecyclePhase.PACKAGE)
public class RunBambooSpecsMojo extends AbstractMojo {
    @Parameter(defaultValue = "${project}", required = true, readonly = true)
    private MavenProject project;

    @Parameter(defaultValue = "${project.build.outputDirectory}", property = "specsDir", required = true)
    private File outputDirectory;

    @Parameter(defaultValue = "false")
    private boolean skip;

    @Parameter(property = "specs.yamlDir")
    private File yamlDir;

    @Parameter(property = "specs.useRest", defaultValue = "true")
    private boolean useRest;

    @Parameter(property = "specs.useSecurityManager", defaultValue = "false")
    private boolean useSecurityManager;

    //Repository-stored Bamboo Specs
    @Parameter(property = "specs.rs.specsSourceId")
    private Long specsSourceId;

    @Override
    public void execute() throws MojoExecutionException {
        if (skip) {
            getLog().debug("Skipping plan execution as the 'skip' property is set to true");
            return;
        }

        getLog().info("Scanning " + outputDirectory + " for classes annotated with Bamboo plan annotation.");

        final URLClassLoader classLoader = createClassLoader();

        final Collection classFiles = getClassFiles(outputDirectory.toPath());

        final SpecsRunner specsRunner = new SpecsRunner(getLog(), classFiles, classLoader, getPriorityClasspath());

        RunnerSettings.setRestEnabled(useRest);
        if (yamlDir!=null) {
            RunnerSettings.setYamlDir(yamlDir.toPath());
        }

        if (specsSourceId !=null) {
            RunnerSettings.setRepositoryStoredSpecsData(new RepositoryStoredSpecsData(specsSourceId));
        }

        specsRunner.runSpecs(useSecurityManager);

        if (yamlDir != null) {
            syncOwnerOfYamlFiles(yamlDir.toPath());
        }
    }

    private Collection getClassFiles(final Path root) throws MojoExecutionException {
        final Collection classFiles;
        try {
            classFiles = BambooSpecsFileScanner.getClassFiles(getLog(), root);
        } catch (final IOException e) {
            throw new MojoExecutionException("Unable to find class files in " + outputDirectory, e);
        }
        return classFiles;
    }

    private URLClassLoader createClassLoader() throws MojoExecutionException {
        final URLClassLoader classLoader;
        try {
            final Stream priorityClasspath = getPriorityClasspath().stream()
                    .map(File::getPath);

            final URL[] runtimeClasspath = Stream.concat(
                    priorityClasspath, project.getRuntimeClasspathElements().stream()
            ).map(this::newUrl)
                    .toArray(URL[]::new);
            classLoader = new URLClassLoader(runtimeClasspath, Thread.currentThread().getContextClassLoader());
        } catch (final DependencyResolutionRequiredException e) {
            throw new MojoExecutionException("Unable to find dependency", e);
        }
        return classLoader;
    }

    private Set getPriorityClasspath() {
        final Artifact specsApi = project.getArtifactMap().get("com.atlassian.bamboo:bamboo-specs-api");
        final Artifact specs = project.getArtifactMap().get("com.atlassian.bamboo:bamboo-specs");
        final Artifact snakeYaml = project.getArtifactMap().get("org.yaml:snakeyaml");
        return Stream.of(specsApi, specs, snakeYaml)
                .map(Artifact::getFile)
                .collect(Collectors.toSet());
    }

    private URL newUrl(final String file) {
        try {
            return new File(file).toURI().toURL();
        } catch (final MalformedURLException e) {
            throw new RuntimeException("Unable to construct classloader", e);
        }
    }

    /**
     * Update YAML files owner to match owner of YAML output directory.
     * It is done outside the part of the mojo run with security manager because we attempt to copy owner from the potentially created YAML output directory.
     * This is done so when mojo is run within Docker container it will produce files that Bamboo server can later remove.
     */
    private void syncOwnerOfYamlFiles(final Path yamlDirectory) throws MojoExecutionException {
        try {
            final UserPrincipal yamlDirectoryOwner = Files.getFileAttributeView(yamlDirectory, FileOwnerAttributeView.class).getOwner();

            getLog().info("Updating ownership of files in " + yamlDirectory + " to " + yamlDirectoryOwner);
            try (Stream walker = Files.walk(yamlDirectory)) {
                    walker.filter(path -> !Objects.equals(yamlDirectory, path))
                        .forEach(updateOwner(yamlDirectoryOwner));
            }
        } catch (IOException e) {
            throw new MojoExecutionException("Unable to read YAML file owner", e);
        }
    }

    private Consumer updateOwner(final UserPrincipal owner) {
        return path -> {
            try {
                Files.getFileAttributeView(path, FileOwnerAttributeView.class).setOwner(owner);
                getLog().debug("Updating owner of" + path);
            } catch (IOException e) {
                throw new RuntimeException("Unable to update YAML file owner", e);
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy