Skip to content

Commit 0b103ee

Browse files
committed
JENKINS-56284 Make compute changelog extensible
1 parent 7e03c01 commit 0b103ee

File tree

4 files changed

+193
-24
lines changed

4 files changed

+193
-24
lines changed

src/main/java/hudson/plugins/git/GitSCM.java

+19-24
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import hudson.model.TaskListener;
3030
import hudson.model.queue.Tasks;
3131
import hudson.plugins.git.browser.GitRepositoryBrowser;
32+
import hudson.plugins.git.extensions.GitClientConflictException;
33+
import hudson.plugins.git.extensions.GitClientType;
34+
import hudson.plugins.git.extensions.GitSCMChangelogExtension;
3235
import hudson.plugins.git.extensions.GitSCMExtension;
3336
import hudson.plugins.git.extensions.GitSCMExtensionDescriptor;
3437
import hudson.plugins.git.extensions.impl.AuthorInChangelog;
@@ -59,6 +62,7 @@
5962
import hudson.util.ListBoxModel;
6063
import jenkins.model.Jenkins;
6164
import jenkins.plugins.git.GitSCMMatrixUtil;
65+
import jenkins.plugins.git.LegacyGitSCMChangelogExtension;
6266
import net.sf.json.JSONObject;
6367

6468
import org.eclipse.jgit.errors.MissingObjectException;
@@ -1247,7 +1251,7 @@ public void checkout(Run<?, ?> build, Launcher launcher, FilePath workspace, Tas
12471251

12481252
if (changelogFile != null) {
12491253
computeChangeLog(git, revToBuild.revision, listener, previousBuildData, new FilePath(changelogFile),
1250-
new BuildChooserContextImpl(build.getParent(), build, environment));
1254+
build);
12511255
}
12521256
}
12531257

@@ -1266,6 +1270,7 @@ private void printCommitMessageToLog(TaskListener listener, GitClient git, final
12661270
}
12671271
}
12681272

1273+
// TODO update JavaDoc
12691274
/**
12701275
* Build up change log from all the branches that we've merged into {@code revToBuild}.
12711276
*
@@ -1308,33 +1313,16 @@ private void printCommitMessageToLog(TaskListener listener, GitClient git, final
13081313
* Information that captures what we did during the last build. We need this for changelog,
13091314
* or else we won't know where to stop.
13101315
*/
1311-
private void computeChangeLog(GitClient git, Revision revToBuild, TaskListener listener, BuildData previousBuildData, FilePath changelogFile, BuildChooserContext context) throws IOException, InterruptedException {
1316+
private void computeChangeLog(GitClient git, Revision revToBuild, TaskListener listener, BuildData previousBuildData, FilePath changelogFile, Run<?, ?> build) throws IOException, InterruptedException {
13121317
boolean executed = false;
13131318
ChangelogCommand changelog = git.changelog();
1314-
changelog.includes(revToBuild.getSha1());
1319+
changelog.max(MAX_CHANGELOG); // default to allow override by extensions
13151320
try (Writer out = new OutputStreamWriter(changelogFile.write(),"UTF-8")) {
1316-
boolean exclusion = false;
1317-
ChangelogToBranch changelogToBranch = getExtensions().get(ChangelogToBranch.class);
1318-
if (changelogToBranch != null) {
1319-
listener.getLogger().println("Using 'Changelog to branch' strategy.");
1320-
changelog.excludes(changelogToBranch.getOptions().getRef());
1321-
exclusion = true;
1322-
} else {
1323-
for (Branch b : revToBuild.getBranches()) {
1324-
Build lastRevWas = getBuildChooser().prevBuildForChangelog(b.getName(), previousBuildData, git, context);
1325-
if (lastRevWas != null && lastRevWas.revision != null && git.isCommitInRepo(lastRevWas.getSHA1())) {
1326-
changelog.excludes(lastRevWas.getSHA1());
1327-
exclusion = true;
1328-
}
1329-
}
1330-
}
1321+
GitSCMChangelogExtension ext = getGitSCMChangelogExtension();
1322+
boolean decorated = ext.decorateChangelogCommand(this, build, git, listener, changelog, revToBuild);
13311323

1332-
if (!exclusion) {
1333-
// this is the first time we are building this branch, so there's no base line to compare against.
1334-
// if we force the changelog, it'll contain all the changes in the repo, which is not what we want.
1335-
listener.getLogger().println("First time build. Skipping changelog.");
1336-
} else {
1337-
changelog.to(out).max(MAX_CHANGELOG).execute();
1324+
if (decorated) {
1325+
changelog.to(out).execute();
13381326
executed = true;
13391327
}
13401328
} catch (GitException ge) {
@@ -1344,6 +1332,13 @@ private void computeChangeLog(GitClient git, Revision revToBuild, TaskListener l
13441332
}
13451333
}
13461334

1335+
private GitSCMChangelogExtension getGitSCMChangelogExtension() {
1336+
GitSCMChangelogExtension ext = getExtensions().get(GitSCMChangelogExtension.class);
1337+
if (ext == null)
1338+
ext = new LegacyGitSCMChangelogExtension();
1339+
return ext;
1340+
}
1341+
13471342
@Override
13481343
@Deprecated // Overrides a deprecated implementation, must also be deprecated
13491344
public void buildEnvVars(AbstractBuild<?, ?> build, Map<String, String> env) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package hudson.plugins.git.extensions;
2+
3+
import java.io.IOException;
4+
5+
import hudson.model.Run;
6+
import hudson.model.TaskListener;
7+
import hudson.plugins.git.GitException;
8+
import hudson.plugins.git.GitSCM;
9+
import hudson.plugins.git.Revision;
10+
import org.jenkinsci.plugins.gitclient.ChangelogCommand;
11+
import org.jenkinsci.plugins.gitclient.GitClient;
12+
13+
/**
14+
* FIXME JavaDoc
15+
* @author Zhenlei Huang
16+
*/
17+
public abstract class GitSCMChangelogExtension extends FakeGitSCMExtension {
18+
19+
/**
20+
* Called before a {@link ChangelogCommand} is executed to allow extensions to alter its behaviour.
21+
* @param scm GitSCM object
22+
* @param build run context
23+
* @param git GitClient
24+
* @param listener build log
25+
* @param cmd changelog command to be decorated
26+
* @param revToBuild The revision selected for this build
27+
* @return true in case decorated, false otherwise
28+
* @throws IOException on input or output error
29+
* @throws InterruptedException when interrupted
30+
* @throws GitException on git error
31+
*/
32+
public abstract boolean decorateChangelogCommand(GitSCM scm, Run<?, ?> build, GitClient git, TaskListener listener, ChangelogCommand cmd, Revision revToBuild) throws IOException, InterruptedException, GitException;
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package jenkins.plugins.git;
2+
3+
import java.io.IOException;
4+
import java.io.Serializable;
5+
6+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
7+
import hudson.EnvVars;
8+
import hudson.FilePath;
9+
import hudson.model.Job;
10+
import hudson.model.Run;
11+
import hudson.model.TaskListener;
12+
import hudson.plugins.git.Branch;
13+
import hudson.plugins.git.GitException;
14+
import hudson.plugins.git.GitSCM;
15+
import hudson.plugins.git.Revision;
16+
import hudson.plugins.git.extensions.GitSCMChangelogExtension;
17+
import hudson.plugins.git.util.Build;
18+
import hudson.plugins.git.util.BuildChooserContext;
19+
import hudson.remoting.Channel;
20+
import org.jenkinsci.plugins.gitclient.ChangelogCommand;
21+
import org.jenkinsci.plugins.gitclient.GitClient;
22+
23+
/**
24+
* FIXME JavaDoc
25+
* @author Zhenlei Huang
26+
*/
27+
public class ChangelogToPreviousBuild extends GitSCMChangelogExtension {
28+
29+
@Override
30+
public boolean decorateChangelogCommand(GitSCM scm, Run<?, ?> build, GitClient git, TaskListener listener, ChangelogCommand cmd, Revision revToBuild) throws IOException, InterruptedException, GitException {
31+
boolean decorated = false;
32+
33+
for (Branch b : revToBuild.getBranches()) {
34+
Build lastRevWas = scm.getBuildChooser().prevBuildForChangelog(b.getName(), scm.getBuildData(build.getPreviousBuild()), git, new BuildChooserContextImpl(build.getParent(), build, build.getEnvironment(listener)));
35+
if (lastRevWas != null && lastRevWas.revision != null && git.isCommitInRepo(lastRevWas.getSHA1())) {
36+
cmd.includes(revToBuild.getSha1());
37+
cmd.excludes(lastRevWas.getSHA1());
38+
decorated = true;
39+
}
40+
}
41+
42+
return decorated;
43+
}
44+
45+
// Copy of GitSCM.BuildChooserContextImpl
46+
/*package*/ static class BuildChooserContextImpl implements BuildChooserContext, Serializable {
47+
@SuppressFBWarnings(value="SE_BAD_FIELD", justification="known non-serializable field")
48+
final Job project;
49+
@SuppressFBWarnings(value="SE_BAD_FIELD", justification="known non-serializable field")
50+
final Run build;
51+
final EnvVars environment;
52+
53+
BuildChooserContextImpl(Job project, Run build, EnvVars environment) {
54+
this.project = project;
55+
this.build = build;
56+
this.environment = environment;
57+
}
58+
59+
public <T> T actOnBuild(ContextCallable<Run<?,?>, T> callable) throws IOException, InterruptedException {
60+
return callable.invoke(build, FilePath.localChannel);
61+
}
62+
63+
public <T> T actOnProject(ContextCallable<Job<?,?>, T> callable) throws IOException, InterruptedException {
64+
return callable.invoke(project, FilePath.localChannel);
65+
}
66+
67+
public Run<?, ?> getBuild() {
68+
return build;
69+
}
70+
71+
public EnvVars getEnvironment() {
72+
return environment;
73+
}
74+
75+
private Object writeReplace() {
76+
return Channel.current().export(BuildChooserContext.class,new BuildChooserContext() {
77+
public <T> T actOnBuild(ContextCallable<Run<?,?>, T> callable) throws IOException, InterruptedException {
78+
return callable.invoke(build,Channel.current());
79+
}
80+
81+
public <T> T actOnProject(ContextCallable<Job<?,?>, T> callable) throws IOException, InterruptedException {
82+
return callable.invoke(project,Channel.current());
83+
}
84+
85+
public Run<?, ?> getBuild() {
86+
return build;
87+
}
88+
89+
public EnvVars getEnvironment() {
90+
return environment;
91+
}
92+
});
93+
}
94+
}
95+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package jenkins.plugins.git;
2+
3+
import java.io.IOException;
4+
5+
import hudson.model.Run;
6+
import hudson.model.TaskListener;
7+
import hudson.plugins.git.GitException;
8+
import hudson.plugins.git.GitSCM;
9+
import hudson.plugins.git.Revision;
10+
import hudson.plugins.git.extensions.GitSCMChangelogExtension;
11+
import hudson.plugins.git.extensions.impl.ChangelogToBranch;
12+
import org.jenkinsci.plugins.gitclient.ChangelogCommand;
13+
import org.jenkinsci.plugins.gitclient.GitClient;
14+
15+
16+
/**
17+
* FIXME JavaDoc
18+
* Legacy changelog impl.
19+
*/
20+
public class LegacyGitSCMChangelogExtension extends GitSCMChangelogExtension {
21+
22+
23+
@Override
24+
public boolean decorateChangelogCommand(GitSCM scm, Run<?, ?> build, GitClient git, TaskListener listener, ChangelogCommand cmd, Revision revToBuild) throws IOException, InterruptedException, GitException {
25+
boolean exclusion = false;
26+
27+
// TODO Refactor ChangelogToBranch
28+
ChangelogToBranch changelogToBranch = scm.getExtensions().get(ChangelogToBranch.class);
29+
if (changelogToBranch != null) {
30+
listener.getLogger().println("Using 'Changelog to branch' strategy.");
31+
cmd.includes(revToBuild.getSha1());
32+
cmd.excludes(changelogToBranch.getOptions().getRef());
33+
exclusion = true;
34+
} else {
35+
exclusion = new ChangelogToPreviousBuild().decorateChangelogCommand(scm, build, git, listener, cmd, revToBuild);
36+
}
37+
38+
if (!exclusion) {
39+
// this is the first time we are building this branch, so there's no base line to compare against.
40+
// if we force the changelog, it'll contain all the changes in the repo, which is not what we want.
41+
listener.getLogger().println("First time build. Skipping changelog.");
42+
}
43+
44+
return exclusion;
45+
}
46+
}

0 commit comments

Comments
 (0)