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

Extract for info from Jenkinsfile and support different layout #544

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package io.jenkins.tools.pluginmodernizer.core.extractor;

import io.jenkins.tools.pluginmodernizer.core.model.JDK;
import io.jenkins.tools.pluginmodernizer.core.model.Platform;
import io.jenkins.tools.pluginmodernizer.core.model.PlatformConfig;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
Expand All @@ -13,7 +17,7 @@
import org.slf4j.LoggerFactory;

/**
* Visitor for a Jenkinsfile
* Visitor for a Jenkinsfile to accumulate @see PluginMetadata.
*/
public class JenkinsfileVisitor extends GroovyIsoVisitor<PluginMetadata> {

Expand All @@ -22,6 +26,9 @@ public class JenkinsfileVisitor extends GroovyIsoVisitor<PluginMetadata> {
*/
public static final Logger LOG = LoggerFactory.getLogger(JenkinsfileVisitor.class);

/**
* Keep track of variables if there are defined on top level and not directly on the method invocation.
*/
private final Map<String, Object> variableMap = new HashMap<>();

@Override
Expand All @@ -36,67 +43,198 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations v
return variableDeclarations;
}

@Override
public J visitMapEntry(G.MapEntry mapEntry, PluginMetadata pluginMetadata) {
super.visitMapEntry(mapEntry, pluginMetadata);
J.MethodInvocation method = getCursor().firstEnclosing(J.MethodInvocation.class);
if (method == null) {
return mapEntry;
}
if (method.getSimpleName().equals("buildPlugin")) {
if ("useContainerAgent".equals(mapEntry.getKey().toString())) {
if (mapEntry.getValue() instanceof J.Literal literal) {
pluginMetadata.setUseContainerAgent(Boolean.valueOf(literal.getValueSource()));
} else if (mapEntry.getValue() instanceof J.Identifier) {
Expression resolvedArg = resolveVariable(mapEntry.getValue());
if (resolvedArg instanceof J.Literal literal) {
pluginMetadata.setUseContainerAgent(Boolean.valueOf(literal.getValueSource()));
}
}
}
if ("forkCount".equals(mapEntry.getKey().toString())) {
if (mapEntry.getValue() instanceof J.Literal literal) {
pluginMetadata.setForkCount(literal.getValue().toString());
} else if (mapEntry.getValue() instanceof J.Identifier) {
Expression resolvedArg = resolveVariable(mapEntry.getValue());
if (resolvedArg instanceof J.Literal literal) {
pluginMetadata.setForkCount(literal.getValue().toString());
}
}
}
}
return mapEntry;
}

@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, PluginMetadata pluginMetadata) {
LOG.debug("Visiting method invocation {}", method);
method = super.visitMethodInvocation(method, pluginMetadata);
if ("buildPlugin".equals(method.getSimpleName())) {
List<Expression> args = method.getArguments();

List<Integer> jdkVersions =
args.stream().flatMap(this::extractJdkVersions).distinct().toList();
// Empty args means Java 8 in Windows and Linux
if (args.size() == 1 && args.get(0) instanceof J.Empty) {
pluginMetadata.addPlatform(new PlatformConfig(Platform.LINUX, JDK.JAVA_8, null, true));
pluginMetadata.addPlatform(new PlatformConfig(Platform.WINDOWS, JDK.JAVA_8, null, true));
return method;
}

LOG.info("JDK versions found: {}", jdkVersions);
List<PlatformConfig> platforms = new LinkedList<>();
jonesbusy marked this conversation as resolved.
Show resolved Hide resolved
for (Expression expression : args) {
platforms.addAll(extractPlatforms(expression));
}
// Filter platforms (Remove implicit if there is at least one explicit)
if (platforms.stream().anyMatch(pc -> !pc.implicit())) {
platforms.removeIf(PlatformConfig::implicit);
}
// If all platform are unknown ensure each platform on the list is present with LINUX and WINDOWS
if (platforms.stream().allMatch(pc -> pc.name() == Platform.UNKNOWN)) {
List<PlatformConfig> newPlatforms = new LinkedList<>();
for (PlatformConfig pc : platforms) {
newPlatforms.add(new PlatformConfig(Platform.LINUX, pc.jdk(), null, false));
newPlatforms.add(new PlatformConfig(Platform.WINDOWS, pc.jdk(), null, false));
}
platforms = newPlatforms;
}
// If there is a platform without JDK ensure all all JDK are using the same platform
if (platforms.stream().anyMatch(pc -> pc.jdk() == null)) {
List<PlatformConfig> newPlatforms = new LinkedList<>();
for (PlatformConfig pc : platforms) {
if (!pc.name().equals(Platform.UNKNOWN)) {
if (platforms.stream().allMatch(p -> p.jdk() == null)) {
newPlatforms.add(new PlatformConfig(pc.name(), JDK.JAVA_8, null, false));
continue;
}
for (PlatformConfig pc2 : platforms) {
if (pc2.jdk() != null) {
newPlatforms.add(new PlatformConfig(pc.name(), pc2.jdk(), null, false));
}
}
}
}
platforms = newPlatforms;
}

jdkVersions.forEach(jdkVersion -> pluginMetadata.addJdk(JDK.get(jdkVersion)));
LOG.info("Platform: {}", platforms);
pluginMetadata.setPlatforms(platforms);
}

return method;
}

private Stream<Integer> extractJdkVersions(Expression arg) {
private List<PlatformConfig> extractPlatforms(Expression arg) {
if (arg instanceof G.MapEntry) {
return Stream.of(arg)

// Get 'configurations' stream
Stream<G.MapEntry> configurations = Stream.of(arg)
.map(G.MapEntry.class::cast)
.filter(entry -> "configurations".equals(((J.Literal) entry.getKey()).getValue()))
.filter(entry -> "configurations".equals(((J.Literal) entry.getKey()).getValue()));

// List of platform config from 'configurations' parameter
List<PlatformConfig> platformFromConfigs = configurations
.map(entry -> resolveConfigurations(entry.getValue()))
.filter(value -> value instanceof G.ListLiteral)
.flatMap(value -> ((G.ListLiteral) value).getElements().stream())
.filter(expression -> expression instanceof G.MapLiteral)
.flatMap(expression -> ((G.MapLiteral) expression).getElements().stream())
.filter(mapExpr -> mapExpr instanceof G.MapEntry)
.map(expression -> (G.MapLiteral) expression)
.map(JenkinsfileVisitor::toPlatformEntry)
.toList();

// Cannot be combinated
if (!platformFromConfigs.isEmpty()) {
return platformFromConfigs;
}

// List of JDK versions from 'jdkVersions' parameter
List<Integer> jdkVersions = Stream.of(arg)
.map(G.MapEntry.class::cast)
.filter(mapEntry -> "jdk".equals(((J.Literal) mapEntry.getKey()).getValue()))
.map(mapEntry -> Integer.parseInt(
((J.Literal) mapEntry.getValue()).getValue().toString()));
} else {
.filter(entry -> "jdkVersions".equals(((J.Literal) entry.getKey()).getValue()))
.map(entry -> resolveConfigurations(entry.getValue()))
.filter(value -> value instanceof G.ListLiteral)
.map(value -> (G.ListLiteral) value)
.flatMap(value -> value.getElements().stream())
.map(expression ->
Integer.parseInt(((J.Literal) expression).getValue().toString()))
.toList();

// List of platforms from 'platforms' parameter
List<String> platforms = Stream.of(arg)
.map(G.MapEntry.class::cast)
.filter(entry -> "platforms".equals(((J.Literal) entry.getKey()).getValue()))
.map(entry -> resolveConfigurations(entry.getValue()))
.filter(value -> value instanceof G.ListLiteral)
.map(value -> (G.ListLiteral) value)
.flatMap(value -> value.getElements().stream())
.map(expression -> expression instanceof J.Literal ? expression.toString() : null)
.toList();

// Combine all JDK versions with all platforms
List<PlatformConfig> platformFromJdkVersions = new LinkedList<>();
for (Integer jdk : jdkVersions) {
platformFromJdkVersions.add(new PlatformConfig(Platform.UNKNOWN, JDK.get(jdk), null, true));
}
for (String platform : platforms) {
platformFromJdkVersions.add(new PlatformConfig(Platform.fromPlatform(platform), null, null, true));
}

return platformFromJdkVersions;

} else if (arg instanceof J.Identifier) {
Expression resolvedArg = resolveVariable(arg);
return Stream.of(resolvedArg)
.filter(resolved -> resolved instanceof G.MapLiteral)
.flatMap(resolved -> ((G.MapLiteral) resolved).getElements().stream())
.filter(entry -> entry instanceof G.MapEntry)
.map(G.MapEntry.class::cast)
.filter(entry -> "configurations".equals(((J.Literal) entry.getKey()).getValue()))
.map(entry -> resolveConfigurations(entry.getValue()))
.filter(value -> value instanceof G.ListLiteral)
.flatMap(value -> ((G.ListLiteral) value).getElements().stream())
.filter(expression -> expression instanceof G.MapLiteral)
.flatMap(expression -> ((G.MapLiteral) expression).getElements().stream())
.filter(mapExpr -> mapExpr instanceof G.MapEntry)
.map(G.MapEntry.class::cast)
.filter(mapEntry -> "jdk".equals(((J.Literal) mapEntry.getKey()).getValue()))
.map(mapEntry -> Integer.parseInt(
((J.Literal) mapEntry.getValue()).getValue().toString()));
.map(expression -> (G.MapLiteral) expression)
.map(JenkinsfileVisitor::toPlatformEntry)
.toList();
}
return Collections.emptyList();
}

private Expression resolveVariable(Expression expression) {
if (expression instanceof J.Identifier) {
String variableName = ((J.Identifier) expression).getSimpleName();
if (variableMap.containsKey(variableName)) {
return (Expression) variableMap.get(variableName);
/**
* Return if the map entry is a platform entry.
* @param mapLiteral The map entry
* @return The platform config
*/
private static PlatformConfig toPlatformEntry(G.MapLiteral mapLiteral) {
Stream<G.MapEntry> entries = mapLiteral.getElements().stream();
List<G.MapEntry> list = entries.toList();
Integer jdk = null;
String platform = null;
for (G.MapEntry entry : list) {
if (entry.getKey() instanceof J.Literal key) {
if ("jdk".equals(key.getValue())) {
jdk = Integer.parseInt(entry.getValue().toString());
}
if ("platform".equals(key.getValue())) {
platform = entry.getValue().toString();
}
}
}

return new PlatformConfig(Platform.fromPlatform(platform), JDK.get(jdk), null, false);
}

private Expression resolveVariable(Expression expression) {
String variableName = ((J.Identifier) expression).getSimpleName();
if (variableMap.containsKey(variableName)) {
return (Expression) variableMap.get(variableName);
}
return expression;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,25 @@
import io.jenkins.tools.pluginmodernizer.core.impl.CacheManager;
import io.jenkins.tools.pluginmodernizer.core.model.CacheEntry;
import io.jenkins.tools.pluginmodernizer.core.model.JDK;
import io.jenkins.tools.pluginmodernizer.core.model.Platform;
import io.jenkins.tools.pluginmodernizer.core.model.PlatformConfig;
import io.jenkins.tools.pluginmodernizer.core.model.Plugin;
import io.jenkins.tools.pluginmodernizer.core.model.PreconditionError;
import java.io.Serial;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Metadata of a plugin extracted from its POM file or code
*/
public class PluginMetadata extends CacheEntry<PluginMetadata> implements Serializable {

@Serial
private static final long serialVersionUID = 1L;
public class PluginMetadata extends CacheEntry<PluginMetadata> {

/**
* Name of the plugin
Expand All @@ -46,9 +44,19 @@ public class PluginMetadata extends CacheEntry<PluginMetadata> implements Serial
private List<ArchetypeCommonFile> commonFiles;

/**
* JDK versions supported by the plugin
* List of platforms extracted from Jenkinsfile
*/
private List<PlatformConfig> platforms;

/**
* Use container agent for build extracted from Jenkinsfile
*/
private Set<JDK> jdkVersions;
private Boolean useContainerAgent;

/**
* forkCount extracted from Jenkinsfile
*/
private String forkCount;

/**
* Jenkins version required by the plugin
Expand Down Expand Up @@ -173,21 +181,71 @@ public void setCommonFiles(List<ArchetypeCommonFile> commonFiles) {
}

public Set<JDK> getJdks() {
if (jdkVersions == null) {
jdkVersions = new HashSet<>();
if (platforms == null) {
platforms = new LinkedList<>();
}
return jdkVersions;
return platforms.stream().map(PlatformConfig::jdk).collect(HashSet::new, Set::add, Set::addAll);
}

public void addJdk(JDK jdk) {
if (jdkVersions == null) {
jdkVersions = new HashSet<>();
public Set<Platform> getPlatforms() {
if (platforms == null) {
platforms = new LinkedList<>();
}
jdkVersions.add(jdk);
return platforms.stream().map(PlatformConfig::name).collect(HashSet::new, Set::add, Set::addAll);
}

/**
* Set the JDK versions without platform information
* @param jdkVersions The JDK versions
*/
public void setJdks(Set<JDK> jdkVersions) {
this.jdkVersions = jdkVersions;
if (platforms == null) {
platforms = new ArrayList<>();
}
platforms.addAll(jdkVersions.stream()
.map(jdk -> new PlatformConfig(Platform.UNKNOWN, jdk, null, true))
.toList());
}

public void setPlatforms(List<PlatformConfig> platforms) {
this.platforms = platforms;
}

public void addPlatform(PlatformConfig platform) {
if (platforms == null) {
platforms = new ArrayList<>();
}
platforms.add(platform);
}

public void addPlatform(Platform platform, JDK jdk, String jenkins) {
if (platforms == null) {
platforms = new ArrayList<>();
}
platforms.add(new PlatformConfig(platform, jdk, jenkins, false));
}

public void addError(PreconditionError error) {
if (errors == null) {
errors = new HashSet<>();
}
errors.add(error);
}

public Boolean isUseContainerAgent() {
return useContainerAgent;
}

public void setUseContainerAgent(Boolean useContainerAgent) {
this.useContainerAgent = useContainerAgent;
}

public String getForkCount() {
return forkCount;
}

public void setForkCount(String forkCount) {
this.forkCount = forkCount;
}

/**
Expand Down
Loading
Loading