Skip to content
Permalink
Browse files
[JENKINS-51187] Moving git-changelist-maven-extension into incrementa…
…ls-tools.
  • Loading branch information
jglick committed May 10, 2018
1 parent 49946c5 commit d6aeef7e0d85798ac8473fc029d99c5e5dbc2112
@@ -80,9 +80,9 @@ Finally, configure [git-changelist-maven-extension](https://github.com/jenkinsci
```xml
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
<extension>
<groupId>io.jenkins.tools</groupId>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>git-changelist-maven-extension</artifactId>
<version>1.0-beta-1</version>
<version>1.0-beta-2</version>
</extension>
</extensions>
```
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>parent</artifactId>
<version>1.0-beta-2-SNAPSHOT</version>
</parent>
<artifactId>git-changelist-maven-extension</artifactId>
<name>Git Changelist Maven Extension</name>
<description>Maven extension which can set the special changelist user property according to Git metadata.</description>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>1.7.1</version>
<executions>
<execution>
<goals>
<goal>generate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.9.0.201710071750-r</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,176 @@
/*
* The MIT License
*
* Copyright 2018 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package io.jenkins.tools.incrementals.git_changelist_maven_extension;

import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;

/**
* Sets a {@code changelist} property to a value based on the Git checkout.
* {@code -Dset.changelist} then becomes equivalent to:
* {@code -Dchangelist=-rc$(git rev-list --first-parent --count HEAD).$(git rev-parse --short=12 HEAD)}
* <p>Also does the equivalent of: {@code -DscmTag=$(git rev-parse HEAD)}
* @see <a href="https://maven.apache.org/maven-ci-friendly.html">Maven CI Friendly Versions</a>
* @see <a href="https://maven.apache.org/docs/3.3.1/release-notes.html#Core_Extensions">Core Extensions</a>
*/
@Component(role=AbstractMavenLifecycleParticipant.class, hint="git-changelist-maven-extension")
public class Main extends AbstractMavenLifecycleParticipant {

private static final String IGNORE_DIRT = "ignore.dirt";
private static final int ABBREV_LENGTH = 12;

@Requirement
private Logger log;

@Override
public void afterSessionStart(MavenSession session) throws MavenExecutionException {
Properties props = session.getRequest().getUserProperties();
if ("true".equals(props.getProperty("set.changelist"))) {
if (!props.containsKey("changelist") && !props.containsKey("scmTag")) {
long start = System.nanoTime();
File dir = session.getRequest().getMultiModuleProjectDirectory();
log.debug("running in " + dir);
String fullHash, hash;
int count;
try (Git git = Git.open(dir)) {
Status status = git.status().call();
if (!status.isClean()) {
// Could consider instead making this append a timestamp baased on the most recent file modification.
Set<String> paths = new TreeSet<>(status.getUncommittedChanges());
paths.addAll(status.getUntracked());
String error = "Make sure `git status -s` is empty before using -Dset.changelist: " + paths;
// Note that `git st` does not care about untracked _folders_ so long as there are no relevant _files_ inside them.
if ("true".equals(props.getProperty(IGNORE_DIRT))) {
log.warn(error);
} else {
throw new MavenExecutionException(error + " (use -D" + IGNORE_DIRT + " to make this nonfatal)", (Throwable) null);
}
}
Repository repo = git.getRepository();
ObjectId head = repo.resolve("HEAD");
fullHash = head.name();
hash = head.abbreviate(ABBREV_LENGTH).name();
try (RevWalk walk = new RevWalk(repo)) {
RevCommit headC = walk.parseCommit(head);
count = revCount(walk, headC);
{ // Look for repository commits reachable from HEAD that would clash.
Map<String,List<RevCommit>> encountered = new HashMap<>();
walk.markStart(headC);
int commitCount = 0;
for (RevCommit c : walk) {
commitCount++;
String abbreviated = c.getId().abbreviate(ABBREV_LENGTH).name();
List<RevCommit> earlier = encountered.get(abbreviated);
if (earlier == null) {
earlier = new ArrayList<>(1);
earlier.add(c);
encountered.put(abbreviated, earlier);
} else {
int thisCount = revCount(walk, c);
for (RevCommit other : earlier) {
int otherCount = revCount(walk, other);
if (otherCount == thisCount) {
throw new MavenExecutionException(summarize(c) + " clashes with " + summarize(other) + " as they would both be identified as " + thisCount + "." + abbreviated, (Throwable) null);
} else {
log.info(summarize(c) + " would clash with " + summarize(other) + " except they have differing revcounts: " + thisCount + " vs. " + otherCount);
}
}
}
}
log.debug("Analyzed " + commitCount + " commits for clashes");
}
}
} catch (IOException | GitAPIException x) {
throw new MavenExecutionException("Git operations failed", x);
}
log.debug("Spent " + (System.nanoTime() - start) / 1000 / 1000 + "ms on calculations");
String value = "-rc" + count + "." + hash;
log.info("Setting: -Dchangelist=" + value + " -DscmTag=" + fullHash);
props.setProperty("changelist", value);
props.setProperty("scmTag", fullHash);
} else {
log.info("Declining to override the `changelist` or `scmTag` properties");
}
} else {
log.debug("Skipping Git version setting unless run with -Dset.changelist");
}
}

private static String summarize(RevCommit c) {
return c.getId().name() + "" + c.getShortMessage() + "" + DateTimeFormatter.ISO_LOCAL_DATE.format(Instant.ofEpochSecond(c.getCommitTime()).atZone(ZoneId.systemDefault()));
}

private static int revCount(RevWalk walk, RevCommit c) throws IOException, GitAPIException {
int count = 0;
// https://stackoverflow.com/a/33054511/12916 RevWalk does not seem to provide any easy equivalent to --first-parent, so cannot simply walk.markStart(c) and iterate
while (true) {
count++;
if (c.getParentCount() == 0) {
return count;
} else {
c = walk.parseCommit(c.getParent(0));
}
}
}

@Override
public void afterProjectsRead(MavenSession session) throws MavenExecutionException {
Properties props = session.getRequest().getUserProperties();
if ("true".equals(props.getProperty("set.changelist"))) {
String changelist = props.getProperty("changelist");
for (MavenProject project : session.getProjects()) {
String version = project.getVersion();
if (!version.contains(changelist)) {
log.warn(project.getId() + " does not seem to be including ${changelist} in its <version>");
}
}
}
}

}
@@ -4,7 +4,7 @@
<parent>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>parent</artifactId>
<version>1.0-alpha-4-SNAPSHOT</version>
<version>1.0-beta-2-SNAPSHOT</version>
</parent>
<artifactId>lib</artifactId>
<dependencies>
@@ -4,7 +4,7 @@
<parent>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>parent</artifactId>
<version>1.0-alpha-4-SNAPSHOT</version>
<version>1.0-beta-2-SNAPSHOT</version>
</parent>
<artifactId>incrementals-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
@@ -9,7 +9,7 @@
</parent>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>parent</artifactId>
<version>1.0-alpha-4-SNAPSHOT</version>
<version>1.0-beta-2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Incrementals Tools POM</name>
<description>Tools for working with JEP-305 “Incrementals”.</description>
@@ -32,6 +32,7 @@
<modules>
<module>lib</module>
<module>maven-plugin</module>
<module>git-changelist-maven-extension</module>
</modules>
<build>
<plugins>

0 comments on commit d6aeef7

Please sign in to comment.