Skip to content

Commit

Permalink
JENKINS-56284 Make compute changelog extensible
Browse files Browse the repository at this point in the history
  • Loading branch information
gmshake committed Jan 13, 2020
1 parent ff1a1eb commit 5baf418
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 25 deletions.
45 changes: 20 additions & 25 deletions src/main/java/hudson/plugins/git/GitSCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
import hudson.model.TaskListener;
import hudson.model.queue.Tasks;
import hudson.plugins.git.browser.GitRepositoryBrowser;
import hudson.plugins.git.extensions.GitClientConflictException;
import hudson.plugins.git.extensions.GitClientType;
import hudson.plugins.git.extensions.GitSCMChangelogExtension;
import hudson.plugins.git.extensions.GitSCMExtension;
import hudson.plugins.git.extensions.GitSCMExtensionDescriptor;
import hudson.plugins.git.extensions.impl.AuthorInChangelog;
import hudson.plugins.git.extensions.impl.BuildChooserSetting;
import hudson.plugins.git.extensions.impl.ChangelogToBranch;
import hudson.plugins.git.extensions.impl.PathRestriction;
import hudson.plugins.git.extensions.impl.LocalBranch;
import hudson.plugins.git.extensions.impl.RelativeTargetDirectory;
Expand All @@ -57,6 +59,7 @@
import hudson.util.ListBoxModel;
import jenkins.model.Jenkins;
import jenkins.plugins.git.GitSCMMatrixUtil;
import jenkins.plugins.git.LegacyGitSCMChangelogExtension;
import net.sf.json.JSONObject;

import org.eclipse.jgit.errors.MissingObjectException;
Expand Down Expand Up @@ -1221,7 +1224,7 @@ public void checkout(Run<?, ?> build, Launcher launcher, FilePath workspace, Tas

if (changelogFile != null) {
computeChangeLog(git, revToBuild.revision, listener, previousBuildData, new FilePath(changelogFile),
new BuildChooserContextImpl(build.getParent(), build, environment));
build);
}
}

Expand All @@ -1240,6 +1243,7 @@ private void printCommitMessageToLog(TaskListener listener, GitClient git, final
}
}

// TODO update JavaDoc
/**
* Build up change log from all the branches that we've merged into {@code revToBuild}.
*
Expand Down Expand Up @@ -1282,33 +1286,16 @@ private void printCommitMessageToLog(TaskListener listener, GitClient git, final
* Information that captures what we did during the last build. We need this for changelog,
* or else we won't know where to stop.
*/
private void computeChangeLog(GitClient git, Revision revToBuild, TaskListener listener, BuildData previousBuildData, FilePath changelogFile, BuildChooserContext context) throws IOException, InterruptedException {
private void computeChangeLog(GitClient git, Revision revToBuild, TaskListener listener, BuildData previousBuildData, FilePath changelogFile, Run<?, ?> build) throws IOException, InterruptedException {
boolean executed = false;
ChangelogCommand changelog = git.changelog();
changelog.includes(revToBuild.getSha1());
changelog.max(MAX_CHANGELOG); // default to allow override by extensions
try (Writer out = new OutputStreamWriter(changelogFile.write(),"UTF-8")) {
boolean exclusion = false;
ChangelogToBranch changelogToBranch = getExtensions().get(ChangelogToBranch.class);
if (changelogToBranch != null) {
listener.getLogger().println("Using 'Changelog to branch' strategy.");
changelog.excludes(changelogToBranch.getOptions().getRef());
exclusion = true;
} else {
for (Branch b : revToBuild.getBranches()) {
Build lastRevWas = getBuildChooser().prevBuildForChangelog(b.getName(), previousBuildData, git, context);
if (lastRevWas != null && lastRevWas.revision != null && git.isCommitInRepo(lastRevWas.getSHA1())) {
changelog.excludes(lastRevWas.getSHA1());
exclusion = true;
}
}
}
GitSCMChangelogExtension ext = getGitSCMChangelogExtension();
boolean decorated = ext.decorateChangelogCommand(this, build, git, listener, changelog, revToBuild);

if (!exclusion) {
// this is the first time we are building this branch, so there's no base line to compare against.
// if we force the changelog, it'll contain all the changes in the repo, which is not what we want.
listener.getLogger().println("First time build. Skipping changelog.");
} else {
changelog.to(out).max(MAX_CHANGELOG).execute();
if (decorated) {
changelog.to(out).execute();
executed = true;
}
} catch (GitException ge) {
Expand All @@ -1318,6 +1305,14 @@ private void computeChangeLog(GitClient git, Revision revToBuild, TaskListener l
}
}

private GitSCMChangelogExtension getGitSCMChangelogExtension() {
GitSCMChangelogExtension ext = getExtensions().get(GitSCMChangelogExtension.class);
if (ext == null)
ext = new LegacyGitSCMChangelogExtension();
return ext;
}

@Override
public void buildEnvVars(AbstractBuild<?, ?> build, Map<String, String> env) {
buildEnvironment(build, env);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package hudson.plugins.git.extensions;

import java.io.IOException;

import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.git.GitException;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.Revision;
import org.jenkinsci.plugins.gitclient.ChangelogCommand;
import org.jenkinsci.plugins.gitclient.GitClient;

/**
* FIXME JavaDoc
* @author Zhenlei Huang
*/
public abstract class GitSCMChangelogExtension extends FakeGitSCMExtension {

/**
* Called before a {@link ChangelogCommand} is executed to allow extensions to alter its behaviour.
* @param scm GitSCM object
* @param build run context
* @param git GitClient
* @param listener build log
* @param cmd changelog command to be decorated
* @param revToBuild The revision selected for this build
* @return true in case decorated, false otherwise
* @throws IOException on input or output error
* @throws InterruptedException when interrupted
* @throws GitException on git error
*/
public abstract boolean decorateChangelogCommand(GitSCM scm, Run<?, ?> build, GitClient git, TaskListener listener, ChangelogCommand cmd, Revision revToBuild) throws IOException, InterruptedException, GitException;
}
95 changes: 95 additions & 0 deletions src/main/java/jenkins/plugins/git/ChangelogToPreviousBuild.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package jenkins.plugins.git;

import java.io.IOException;
import java.io.Serializable;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.git.Branch;
import hudson.plugins.git.GitException;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.Revision;
import hudson.plugins.git.extensions.GitSCMChangelogExtension;
import hudson.plugins.git.util.Build;
import hudson.plugins.git.util.BuildChooserContext;
import hudson.remoting.Channel;
import org.jenkinsci.plugins.gitclient.ChangelogCommand;
import org.jenkinsci.plugins.gitclient.GitClient;

/**
* FIXME JavaDoc
* @author Zhenlei Huang
*/
public class ChangelogToPreviousBuild extends GitSCMChangelogExtension {

@Override
public boolean decorateChangelogCommand(GitSCM scm, Run<?, ?> build, GitClient git, TaskListener listener, ChangelogCommand cmd, Revision revToBuild) throws IOException, InterruptedException, GitException {
boolean decorated = false;

for (Branch b : revToBuild.getBranches()) {
Build lastRevWas = scm.getBuildChooser().prevBuildForChangelog(b.getName(), scm.getBuildData(build.getPreviousBuild()), git, new BuildChooserContextImpl(build.getParent(), build, build.getEnvironment(listener)));
if (lastRevWas != null && lastRevWas.revision != null && git.isCommitInRepo(lastRevWas.getSHA1())) {
cmd.includes(revToBuild.getSha1());
cmd.excludes(lastRevWas.getSHA1());
decorated = true;
}
}

return decorated;
}

// Copy of GitSCM.BuildChooserContextImpl
/*package*/ static class BuildChooserContextImpl implements BuildChooserContext, Serializable {
@SuppressFBWarnings(value="SE_BAD_FIELD", justification="known non-serializable field")
final Job project;
@SuppressFBWarnings(value="SE_BAD_FIELD", justification="known non-serializable field")
final Run build;
final EnvVars environment;

BuildChooserContextImpl(Job project, Run build, EnvVars environment) {
this.project = project;
this.build = build;
this.environment = environment;
}

public <T> T actOnBuild(ContextCallable<Run<?,?>, T> callable) throws IOException, InterruptedException {
return callable.invoke(build, FilePath.localChannel);
}

public <T> T actOnProject(ContextCallable<Job<?,?>, T> callable) throws IOException, InterruptedException {
return callable.invoke(project, FilePath.localChannel);
}

public Run<?, ?> getBuild() {
return build;
}

public EnvVars getEnvironment() {
return environment;
}

private Object writeReplace() {
return Channel.current().export(BuildChooserContext.class,new BuildChooserContext() {
public <T> T actOnBuild(ContextCallable<Run<?,?>, T> callable) throws IOException, InterruptedException {
return callable.invoke(build,Channel.current());
}

public <T> T actOnProject(ContextCallable<Job<?,?>, T> callable) throws IOException, InterruptedException {
return callable.invoke(project,Channel.current());
}

public Run<?, ?> getBuild() {
return build;
}

public EnvVars getEnvironment() {
return environment;
}
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package jenkins.plugins.git;

import java.io.IOException;

import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.git.GitException;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.Revision;
import hudson.plugins.git.extensions.GitSCMChangelogExtension;
import hudson.plugins.git.extensions.impl.ChangelogToBranch;
import org.jenkinsci.plugins.gitclient.ChangelogCommand;
import org.jenkinsci.plugins.gitclient.GitClient;


/**
* FIXME JavaDoc
* Legacy changelog impl.
*/
public class LegacyGitSCMChangelogExtension extends GitSCMChangelogExtension {


@Override
public boolean decorateChangelogCommand(GitSCM scm, Run<?, ?> build, GitClient git, TaskListener listener, ChangelogCommand cmd, Revision revToBuild) throws IOException, InterruptedException, GitException {
boolean exclusion = false;

// TODO Refactor ChangelogToBranch
ChangelogToBranch changelogToBranch = scm.getExtensions().get(ChangelogToBranch.class);
if (changelogToBranch != null) {
listener.getLogger().println("Using 'Changelog to branch' strategy.");
cmd.includes(revToBuild.getSha1());
cmd.excludes(changelogToBranch.getOptions().getRef());
exclusion = true;
} else {
exclusion = new ChangelogToPreviousBuild().decorateChangelogCommand(scm, build, git, listener, cmd, revToBuild);
}

if (!exclusion) {
// this is the first time we are building this branch, so there's no base line to compare against.
// if we force the changelog, it'll contain all the changes in the repo, which is not what we want.
listener.getLogger().println("First time build. Skipping changelog.");
}

return exclusion;
}
}

0 comments on commit 5baf418

Please sign in to comment.