Skip to content

Commit

Permalink
[JENKINS-46368] Trigger downstream pipeline on Maven parent pom changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Cyrille Le Clerc committed Dec 14, 2017
1 parent 8d889da commit 5aba93f
Show file tree
Hide file tree
Showing 16 changed files with 398 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Jenkinsfile
@@ -1,4 +1,4 @@
/*
* `buildPlugin` step provided by: https://github.com/jenkins-infra/pipeline-library
*/
buildPlugin(platforms: ['linux'])
buildPlugin(platforms: ['highmem'])
Expand Up @@ -53,6 +53,19 @@ void recordDependency(@Nonnull String jobFullName, int buildNumber,
@Nonnull String groupId, @Nonnull String artifactId, @Nonnull String version, @Nonnull String type, @Nonnull String scope,
boolean ignoreUpstreamTriggers);

/**
* Record a Maven parent project of a pom processed by this build of a build.
*
* @param jobFullName see {@link Item#getFullName()}
* @param buildNumber see {@link Run#getNumber()}
* @param parentGroupId Maven dependency groupId
* @param parentArtifactId Maven dependency artifactId
* @param parentVersion Maven dependency version
* @param ignoreUpstreamTriggers see {@link PipelineGraphPublisher#isIgnoreUpstreamTriggers()} ()}
*/
void recordParentProject(@Nonnull String jobFullName, int buildNumber,
@Nonnull String parentGroupId, @Nonnull String parentArtifactId, @Nonnull String parentVersion,
boolean ignoreUpstreamTriggers);
/**
* Record a Maven artifact generated in a build.
*
Expand Down
Expand Up @@ -121,6 +121,27 @@ public void recordDependency(String jobFullName, int buildNumber, String groupId
}
}

@Override
public void recordParentProject(@Nonnull String jobFullName, int buildNumber, @Nonnull String parentGroupId, @Nonnull String parentArtifactId, @Nonnull String parentVersion, boolean ignoreUpstreamTriggers) {
LOGGER.log(Level.FINE, "recordParentProject({0}#{1}, {2}:{3} ignoreUpstreamTriggers:{5}})",
new Object[]{jobFullName, buildNumber, parentGroupId, parentArtifactId, parentVersion, ignoreUpstreamTriggers});
long buildPrimaryKey = getOrCreateBuildPrimaryKey(jobFullName, buildNumber);
long parentArtifactPrimaryKey = getOrCreateArtifactPrimaryKey(parentGroupId, parentArtifactId, parentVersion, "pom");

try (Connection cnn = jdbcConnectionPool.getConnection()) {
cnn.setAutoCommit(false);
try (PreparedStatement stmt = cnn.prepareStatement("INSERT INTO MAVEN_PARENT_PROJECT(ARTIFACT_ID, BUILD_ID, IGNORE_UPSTREAM_TRIGGERS) VALUES (?, ?, ?)")) {
stmt.setLong(1, parentArtifactPrimaryKey);
stmt.setLong(2, buildPrimaryKey);
stmt.setBoolean(3, ignoreUpstreamTriggers);
stmt.execute();
}
cnn.commit();
} catch (SQLException e) {
throw new RuntimeSqlException(e);
}
}

@Override
public void recordGeneratedArtifact(String jobFullName, int buildNumber, String groupId, String artifactId, String version, String type, String baseVersion, boolean skipDownstreamTriggers) {
LOGGER.log(Level.FINE, "recordGeneratedArtifact({0}#{1}, {2}:{3}:{4}:{5}, version:{6}, skipDownstreamTriggers:{7})",
Expand Down Expand Up @@ -392,7 +413,7 @@ protected int getSchemaVersion(Connection cnn) throws SQLException {
*/
protected synchronized void testDatabase() throws RuntimeSqlException {
try (Connection cnn = jdbcConnectionPool.getConnection()) {
List<String> tables = Arrays.asList("MAVEN_ARTIFACT", "JENKINS_JOB", "JENKINS_BUILD", "MAVEN_DEPENDENCY", "GENERATED_MAVEN_ARTIFACT");
List<String> tables = Arrays.asList("MAVEN_ARTIFACT", "JENKINS_JOB", "JENKINS_BUILD", "MAVEN_DEPENDENCY", "GENERATED_MAVEN_ARTIFACT", "MAVEN_PARENT_PROJECT");
for (String table : tables) {
try (Statement stmt = cnn.createStatement()) {
try (ResultSet rst = stmt.executeQuery("SELECT count(*) FROM " + table)) {
Expand All @@ -415,6 +436,12 @@ protected synchronized void testDatabase() throws RuntimeSqlException {
@Nonnull
@Override
public List<String> listDownstreamJobs(@Nonnull String jobFullName, int buildNumber) {
List<String> downstreamJobs = listDownstreamPipelinesBasedOnMavenDependencies(jobFullName, buildNumber);
downstreamJobs.addAll(listDownstreamPipelinesBasedOnParentProjectDependencies(jobFullName, buildNumber));
return downstreamJobs;
}

protected List<String> listDownstreamPipelinesBasedOnMavenDependencies(@Nonnull String jobFullName, int buildNumber) {
LOGGER.log(Level.FINER, "listDownstreamJobs({0}, {1})", new Object[]{jobFullName, buildNumber});
String generatedArtifactsSql = "SELECT DISTINCT GENERATED_MAVEN_ARTIFACT.ARTIFACT_ID " +
" FROM GENERATED_MAVEN_ARTIFACT " +
Expand Down Expand Up @@ -456,6 +483,48 @@ public List<String> listDownstreamJobs(@Nonnull String jobFullName, int buildNum
return downstreamJobsFullNames;
}

protected List<String> listDownstreamPipelinesBasedOnParentProjectDependencies(@Nonnull String jobFullName, int buildNumber) {
LOGGER.log(Level.FINER, "listDownstreamPipelinesBasedOnParentProjectDependencies({0}, {1})", new Object[]{jobFullName, buildNumber});
String generatedArtifactsSql = "SELECT DISTINCT GENERATED_MAVEN_ARTIFACT.ARTIFACT_ID " +
" FROM GENERATED_MAVEN_ARTIFACT " +
" INNER JOIN JENKINS_BUILD AS UPSTREAM_BUILD ON GENERATED_MAVEN_ARTIFACT.BUILD_ID = UPSTREAM_BUILD.ID " +
" INNER JOIN JENKINS_JOB AS UPSTREAM_JOB ON UPSTREAM_BUILD.JOB_ID = UPSTREAM_JOB.ID " +
" WHERE " +
" UPSTREAM_JOB.FULL_NAME = ? AND" +
" UPSTREAM_BUILD.NUMBER = ? AND " +
" GENERATED_MAVEN_ARTIFACT.SKIP_DOWNSTREAM_TRIGGERS = FALSE";

String sql = "SELECT DISTINCT DOWNSTREAM_JOB.FULL_NAME " +
" FROM JENKINS_JOB AS DOWNSTREAM_JOB" +
" INNER JOIN JENKINS_BUILD AS DOWNSTREAM_BUILD ON DOWNSTREAM_JOB.ID = DOWNSTREAM_BUILD.JOB_ID " +
" INNER JOIN MAVEN_PARENT_PROJECT ON DOWNSTREAM_BUILD.ID = MAVEN_PARENT_PROJECT.BUILD_ID" +
" WHERE " +
" MAVEN_PARENT_PROJECT.ARTIFACT_ID IN (" + generatedArtifactsSql + ") AND " +
" MAVEN_PARENT_PROJECT.IGNORE_UPSTREAM_TRIGGERS = FALSE AND " +
" DOWNSTREAM_BUILD.NUMBER in (SELECT MAX(JENKINS_BUILD.NUMBER) FROM JENKINS_BUILD WHERE DOWNSTREAM_JOB.ID = JENKINS_BUILD.JOB_ID)" +
" ORDER BY DOWNSTREAM_JOB.FULL_NAME";

List<String> downstreamJobsFullNames = new ArrayList<>();
LOGGER.log(Level.FINER, "sql: {0}, jobFullName:{1}, buildNumber: {2}", new Object[]{sql, jobFullName, buildNumber});

try (Connection cnn = jdbcConnectionPool.getConnection()) {
try (PreparedStatement stmt = cnn.prepareStatement(sql)) {
stmt.setString(1, jobFullName);
stmt.setInt(2, buildNumber);
try (ResultSet rst = stmt.executeQuery()) {
while (rst.next()) {
downstreamJobsFullNames.add(rst.getString(1));
}
}
}
} catch (SQLException e) {
throw new RuntimeSqlException(e);
}
LOGGER.log(Level.FINE, "listDownstreamPipelinesBasedOnParentProjectDependencies({0}, {1}): {2}", new Object[]{jobFullName, buildNumber, downstreamJobsFullNames});

return downstreamJobsFullNames;
}

/**
* List the artifacts generated by the given build
*
Expand Down Expand Up @@ -507,7 +576,7 @@ public String toPrettyString() {
List<String> prettyStrings = new ArrayList<>();
try (Connection cnn = jdbcConnectionPool.getConnection()) {
prettyStrings.add("jdbc.url: " + cnn.getMetaData().getURL());
List<String> tables = Arrays.asList("MAVEN_ARTIFACT", "JENKINS_JOB", "JENKINS_BUILD", "MAVEN_DEPENDENCY", "GENERATED_MAVEN_ARTIFACT");
List<String> tables = Arrays.asList("MAVEN_ARTIFACT", "JENKINS_JOB", "JENKINS_BUILD", "MAVEN_DEPENDENCY", "GENERATED_MAVEN_ARTIFACT", "MAVEN_PARENT_PROJECT");
for (String table : tables) {
try (Statement stmt = cnn.createStatement()) {
try (ResultSet rst = stmt.executeQuery("SELECT count(*) FROM " + table)) {
Expand Down
Expand Up @@ -38,6 +38,11 @@ public void recordDependency(String jobFullName, int buildNumber, String groupId

}

@Override
public void recordParentProject(@Nonnull String jobFullName, int buildNumber, @Nonnull String parentGroupId, @Nonnull String parentArtifactId, @Nonnull String parentVersion, boolean ignoreUpstreamTriggers) {

}

@Override
public void recordGeneratedArtifact(String jobFullName, int buildNumber, String groupId, String artifactId, String version, String type, String baseVersion, boolean skipDownstreamTriggers) {

Expand Down
Expand Up @@ -14,6 +14,7 @@

/**
* List dependencies from the spy log.
*
* @author <a href="mailto:cleclerc@cloudbees.com">Cyrille Le Clerc</a>
*/
public class DependenciesLister {
Expand All @@ -24,7 +25,7 @@ public class DependenciesLister {
*/
@Nonnull
public static List<MavenSpyLogProcessor.MavenDependency> listDependencies(final Element mavenSpyLogs,
final Logger logger) {
final Logger logger) {

final List<MavenSpyLogProcessor.MavenDependency> result = new ArrayList<>();

Expand Down Expand Up @@ -58,4 +59,36 @@ public static List<MavenSpyLogProcessor.MavenDependency> listDependencies(final
return result;
}

/**
* @param mavenSpyLogs Root XML element
* @return list of {@link MavenSpyLogProcessor.MavenArtifact}
*/
@Nonnull
public static List<MavenSpyLogProcessor.MavenArtifact> listParentProjects(final Element mavenSpyLogs,
final Logger logger) {

final List<MavenSpyLogProcessor.MavenArtifact> result = new ArrayList<>();

for (final Element dependencyResolutionResult : XmlUtils.getExecutionEvents(mavenSpyLogs,
"ProjectStarted")) {
final Element parentProjectElt = XmlUtils.getUniqueChildElementOrNull(
dependencyResolutionResult, "parentProject");

if (parentProjectElt == null) {
continue;
}
final MavenSpyLogProcessor.MavenArtifact parentProject = new MavenSpyLogProcessor.MavenArtifact();

parentProject.groupId = parentProjectElt.getAttribute("groupId");
parentProject.artifactId = parentProjectElt.getAttribute("artifactId");
parentProject.version = parentProjectElt.getAttribute("version");
parentProject.baseVersion = parentProject.version;
parentProject.snapshot = parentProject.version.endsWith("-SNAPSHOT");

result.add(parentProject);
}

return result;
}

}
@@ -1,7 +1,5 @@
package org.jenkinsci.plugins.pipeline.maven.publishers;

import static org.jenkinsci.plugins.pipeline.maven.publishers.DependenciesLister.listDependencies;

import hudson.Extension;
import hudson.model.Run;
import hudson.model.TaskListener;
Expand All @@ -28,6 +26,8 @@

import javax.annotation.Nonnull;

import static org.jenkinsci.plugins.pipeline.maven.publishers.DependenciesLister.*;

/**
* Fingerprint the dependencies of the maven project.
*
Expand Down Expand Up @@ -85,10 +85,57 @@ public void process(@Nonnull StepContext context, @Nonnull Element mavenSpyLogsE

PipelineMavenPluginDao dao = GlobalPipelineMavenConfig.get().getDao();

recordParentProject(mavenSpyLogsElt, run,listener, dao);
recordDependencies(mavenSpyLogsElt, run, listener, dao);
recordGeneratedArtifacts(mavenSpyLogsElt, run, listener, dao);
}

protected void recordParentProject(@Nonnull Element mavenSpyLogsElt, @Nonnull Run run, @Nonnull TaskListener listener, @Nonnull PipelineMavenPluginDao dao) {
List<MavenSpyLogProcessor.MavenArtifact> parentProjects = listParentProjects(mavenSpyLogsElt, LOGGER);
recordParentProject(parentProjects, run, listener, dao);
}

protected void recordParentProject(List<MavenSpyLogProcessor.MavenArtifact> parentProjects, @Nonnull Run run, @Nonnull TaskListener listener, @Nonnull PipelineMavenPluginDao dao) {
if (LOGGER.isLoggable(Level.FINE)) {
listener.getLogger().println("[withMaven] pipelineGraphPublisher - recordParentProject - filter: " +
"versions[snapshot: " + isIncludeSnapshotVersions() + ", release: " + isIncludeReleaseVersions() + "]");
}

for (MavenSpyLogProcessor.MavenArtifact parentProject : parentProjects) {
if (parentProject.snapshot) {
if (!includeSnapshotVersions) {
if (LOGGER.isLoggable(Level.FINER)) {
listener.getLogger().println("[withMaven] pipelineGraphPublisher - Skip recording snapshot parent project: " + parentProject.getId());
}
continue;
}
} else {
if (!includeReleaseVersions) {
if (LOGGER.isLoggable(Level.FINER)) {
listener.getLogger().println("[withMaven] pipelineGraphPublisher - Skip recording release parent project: " + parentProject.getId());
}
continue;
}
}

try {
if (LOGGER.isLoggable(Level.FINE)) {
listener.getLogger().println("[withMaven] pipelineGraphPublisher - Record parent project: " + parentProject.getId() + ", ignoreUpstreamTriggers: " + ignoreUpstreamTriggers);
}

dao.recordParentProject(run.getParent().getFullName(), run.getNumber(),
parentProject.groupId, parentProject.artifactId, parentProject.version,
this.ignoreUpstreamTriggers);

} catch (RuntimeException e) {
listener.error("[withMaven] pipelineGraphPublisher - WARNING: Exception recording parent project " + parentProject.getId() + " on build, skip");
e.printStackTrace(listener.getLogger());
listener.getLogger().flush();
}
}

}

protected void recordDependencies(@Nonnull Element mavenSpyLogsElt, @Nonnull Run run, @Nonnull TaskListener listener, @Nonnull PipelineMavenPluginDao dao) {
List<MavenSpyLogProcessor.MavenDependency> dependencies = listDependencies(mavenSpyLogsElt, LOGGER);
recordDependencies(dependencies, run, listener, dao);
Expand Down
18 changes: 18 additions & 0 deletions jenkins-plugin/src/main/resources/sql/h2/05_migration.sql
@@ -0,0 +1,18 @@
CREATE TABLE MAVEN_PARENT_PROJECT
(
ARTIFACT_ID integer NOT NULL,
BUILD_ID integer NOT NULL,
ID integer AUTO_INCREMENT PRIMARY KEY NOT NULL,
IGNORE_UPSTREAM_TRIGGERS BOOLEAN DEFAULT FALSE,
CONSTRAINT PARENT_PROJECT_BUILD_ID_FK FOREIGN KEY (BUILD_ID) REFERENCES JENKINS_BUILD (ID) ON DELETE CASCADE,
CONSTRAINT PARENT_PROJECT_ARTIFACT_ID_FK FOREIGN KEY (ARTIFACT_ID) REFERENCES MAVEN_ARTIFACT(ID) ON DELETE CASCADE
);


UPDATE VERSION SET VERSION = 5;






Expand Up @@ -53,6 +53,14 @@ protected void loadMavenJarProjectInGitRepo(GitSampleRepoRule gitRepo) throws Ex
loadSourceCodeInGitRepository(gitRepo, "/org/jenkinsci/plugins/pipeline/maven/test/test_maven_projects/maven_jar_project/");
}

protected void loadMavenPomProjectInGitRepo(GitSampleRepoRule gitRepo) throws Exception {
loadSourceCodeInGitRepository(gitRepo, "/org/jenkinsci/plugins/pipeline/maven/test/test_maven_projects/maven_pom_project/");
}

protected void loadMavenJarWithParentPomProjectInGitRepo(GitSampleRepoRule gitRepo) throws Exception {
loadSourceCodeInGitRepository(gitRepo, "/org/jenkinsci/plugins/pipeline/maven/test/test_maven_projects/maven_jar_with_parent_pom_project/");
}

protected void loadMavenWarProjectInGitRepo(GitSampleRepoRule gitRepo) throws Exception {
loadSourceCodeInGitRepository(gitRepo, "/org/jenkinsci/plugins/pipeline/maven/test/test_maven_projects/maven_war_project/");
}
Expand Down

0 comments on commit 5aba93f

Please sign in to comment.