Skip to content
Permalink
Browse files

[FIXED JENKINS-12162] Mercurial plugin does not honor subdirectory.

  • Loading branch information
Jesse Glick committed Jan 4, 2012
1 parent 25e2ca4 commit 022276b99683d23ccc72afd2c78ce28e4af01ef9
@@ -8,6 +8,7 @@
import hudson.Launcher;
import hudson.Launcher.ProcStarter;
import hudson.Util;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.TaskListener;
import hudson.model.AbstractBuild;
@@ -224,7 +225,7 @@ public SCMRevisionState calcRevisionsFromBuild(AbstractBuild<?, ?> build, Launch
// tag action is added during checkout, so this shouldn't be called, but just in case.
HgExe hg = new HgExe(this, launcher, build, listener, build.getEnvironment(listener));
String tip = hg.tip(workspace2Repo(build.getWorkspace()), null);
return tip != null ? new MercurialTagAction(tip) : null;
return tip != null ? new MercurialTagAction(tip, subdir) : null;
}

@Override
@@ -249,11 +250,11 @@ protected PollingResult compareRemoteRevisionWith(AbstractProject<?, ?> project,
throw new IOException("failed to find ID of branch head");
}
if (remote.equals(baseline.id)) { // shortcut
return new PollingResult(baseline, new MercurialTagAction(remote), Change.NONE);
return new PollingResult(baseline, new MercurialTagAction(remote, subdir), Change.NONE);
}
Set<String> changedFileNames = parseStatus(hg.popen(repository, listener, false, new ArgumentListBuilder("status", "--rev", baseline.id, "--rev", remote)));

MercurialTagAction cur = new MercurialTagAction(remote);
MercurialTagAction cur = new MercurialTagAction(remote, subdir);
return new PollingResult(baseline,cur,computeDegreeOfChanges(changedFileNames,output));
} catch(IOException e) {
if (causedByMissingHg(e)) {
@@ -423,7 +424,7 @@ private boolean canReuseWorkspace(FilePath repo,
private void determineChanges(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener, File changelogFile, FilePath repository) throws IOException, InterruptedException {

AbstractBuild<?, ?> previousBuild = build.getPreviousBuild();
MercurialTagAction prevTag = previousBuild != null ? previousBuild.getAction(MercurialTagAction.class) : null;
MercurialTagAction prevTag = previousBuild != null ? findTag(previousBuild) : null;
if (prevTag == null) {
listener.getLogger().println("WARN: Revision data for previous build unavailable; unable to determine change log");
createEmptyChangeLog(changelogFile, listener, "changelog");
@@ -525,7 +526,7 @@ private void update(AbstractBuild<?, ?> build, Launcher launcher, FilePath repos

String tip = hg.tip(repository, null);
if (tip != null) {
build.addAction(new MercurialTagAction(tip));
build.addAction(new MercurialTagAction(tip, subdir));
}
}

@@ -604,23 +605,40 @@ private void clone(AbstractBuild<?,?> build, Launcher launcher, FilePath reposit

String tip = hg.tip(repository, null);
if (tip != null) {
build.addAction(new MercurialTagAction(tip));
build.addAction(new MercurialTagAction(tip, subdir));
}
}

@Override
public void buildEnvVars(AbstractBuild<?,?> build, Map<String, String> env) {
MercurialTagAction a = build.getAction(MercurialTagAction.class);
MercurialTagAction a = findTag(build);
if (a != null) {
env.put("MERCURIAL_REVISION", a.id);
}
}

private MercurialTagAction findTag(AbstractBuild<?, ?> build) {
for (Action action : build.getActions()) {
if (action instanceof MercurialTagAction) {
MercurialTagAction tag = (MercurialTagAction) action;
// JENKINS-12162: differentiate plugins in different subdirs
if ((subdir == null && tag.subdir == null) || (subdir != null && subdir.equals(tag.subdir))) {
return tag;
}
}
}
return null;
}

@Override
public ChangeLogParser createChangeLogParser() {
return new MercurialChangeLogParser(_modules);
}

@Override public FilePath getModuleRoot(FilePath workspace, AbstractBuild build) {
return workspace2Repo(workspace);
}

@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
@@ -1,6 +1,7 @@
package hudson.plugins.mercurial;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.model.AbstractBuild;
import hudson.scm.SCMRevisionState;
import org.kohsuke.stapler.export.Exported;
@@ -22,15 +23,26 @@
*/
public final String id;

public MercurialTagAction(@NonNull String id) {
/**
* Matches {@link MercurialSCM#subdir}.
*/
public final String subdir;

public MercurialTagAction(@NonNull String id, @Nullable String subdir) {
this.id = id;
this.subdir = subdir;
}

@Exported(name = "mercurialNodeName")
public String getId() {
return id;
}

@Exported
public String getSubdir() {
return subdir;
}

/**
* Mercurial often uses a short ID form that consists of 12 letters.
*/
@@ -1,19 +1,22 @@
package hudson.plugins.mercurial;

import hudson.FilePath;
import hudson.Launcher;
import hudson.Proc;
import hudson.model.AbstractBuild;
import hudson.model.Cause.UserCause;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.ParametersAction;
import hudson.model.Result;
import hudson.model.StringParameterValue;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.scm.PollingResult;
import hudson.scm.SCM;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -438,6 +441,29 @@ public void testChangelogFromPreviousBuild() throws Exception {
Collections.singletonList(Collections.singleton("dir3/f1")), b);
}

@Bug(12162)
public void testChangelogInMultiSCM() throws Exception {
FreeStyleProject p = createFreeStyleProject();
hg(repo, "init");
touchAndCommit(repo, "r1f1");
File repo2 = createTmpDir();
hg(repo2, "init");
touchAndCommit(repo2, "r2f1");
p.setScm(new MultiSCM(Arrays.<SCM>asList(
new MercurialSCM(hgInstallation, repo.getPath(), null, null, "r1", null, false),
new MercurialSCM(hgInstallation, repo2.getPath(), null, null, "r2", null, false))));
FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new UserCause()).get());
touchAndCommit(repo, "r1f2");
touchAndCommit(repo2, "r2f2");
assertTrue(pollSCMChanges(p).hasChanges());
b = assertBuildStatusSuccess(p.scheduleBuild2(0, new UserCause()).get());
List<Set<String>> paths = new ArrayList<Set<String>>();
// XXX "r1/r1f2" etc. would be preferable; probably requires determineChanges to prepend subdir?
paths.add(Collections.singleton("r1f2"));
paths.add(Collections.singleton("r2f2"));
assertChangeSetPaths(paths, b);
}

public void testPolling() throws Exception {
AbstractBuild<?, ?> b;
PollingResult pr;
@@ -497,15 +523,15 @@ private PretendSlave createNoopPretendSlave() throws Exception {
return createPretendSlave(new NoopFakeLauncher());
}

private void assertChangeSetPaths(List<Set<String>> expectedChangeSetPaths,
AbstractBuild<?, ?> build) {
private void assertChangeSetPaths(List<? extends Set<String>> expectedChangeSetPaths,
AbstractBuild<?, ?> build) throws IOException {
ChangeLogSet<? extends Entry> actualChangeLogSet = build.getChangeSet();
List<Set<String>> actualChangeSetPaths = new LinkedList<Set<String>>();
for (Entry entry : actualChangeLogSet) {
actualChangeSetPaths.add(new LinkedHashSet<String>(entry
.getAffectedPaths()));
}
assertEquals(expectedChangeSetPaths, actualChangeSetPaths);
assertEquals(build.getLog(99).toString(), expectedChangeSetPaths, actualChangeSetPaths);
}

private void assertPollingResult(PollingResult.Change expectedChangeDegree,
@@ -0,0 +1,95 @@
package hudson.plugins.mercurial;

import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.TaskListener;
import hudson.scm.ChangeLogParser;
import hudson.scm.PollingResult;
import hudson.scm.SCM;
import hudson.scm.SCMRevisionState;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;

/**
* Simplified adaptation of code from org.jenkinsci.plugins.multiplescms, for
* testing purposes.
*/
class MultiSCM extends SCM {

private final List<SCM> scms;

MultiSCM(List<SCM> scms) {
this.scms = scms;
}

private static class MultiSCMRevisionState extends SCMRevisionState {
final Map<String,SCMRevisionState> revisionStates = new HashMap<String,SCMRevisionState>();
}

private static String keyFor(SCM scm, FilePath ws, AbstractBuild<?,?> build) { // JENKINS-12298
FilePath[] roots = scm.getModuleRoots(ws, build);
String[] rel = new String[roots.length];
for (int i = 0; i < roots.length; i++) {
rel[i] = roots[i].equals(ws) ? "." : roots[i].getRemote().substring(ws.getRemote().length() + 1);
}
return scm.getType() + Arrays.toString(rel);
}

@Override public SCMRevisionState calcRevisionsFromBuild(AbstractBuild<?,?> build, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
MultiSCMRevisionState revisionStates = new MultiSCMRevisionState();
for (SCM scm : scms) {
SCMRevisionState scmState = scm.calcRevisionsFromBuild(build, launcher, listener);
revisionStates.revisionStates.put(keyFor(scm, build.getWorkspace(), build), scmState);
}
return revisionStates;
}

@Override protected PollingResult compareRemoteRevisionWith(AbstractProject<?,?> project, Launcher launcher, FilePath workspace, TaskListener listener, SCMRevisionState baseline) throws IOException, InterruptedException {
MultiSCMRevisionState baselineStates = baseline instanceof MultiSCMRevisionState ? (MultiSCMRevisionState) baseline : null;
MultiSCMRevisionState currentStates = new MultiSCMRevisionState();
PollingResult.Change overallChange = PollingResult.Change.NONE;
for (SCM scm : scms) {
String key = keyFor(scm, workspace, null);
SCMRevisionState scmBaseline = baselineStates != null ? baselineStates.revisionStates.get(key) : null;
PollingResult scmResult = scm.poll(project, launcher, workspace, listener, scmBaseline != null ? scmBaseline : SCMRevisionState.NONE);
currentStates.revisionStates.put(key, scmResult.remote);
if (scmResult.change.compareTo(overallChange) > 0) {
overallChange = scmResult.change;
}
}
return new PollingResult(baselineStates, currentStates, overallChange);
}

@Override public boolean checkout(AbstractBuild<?, ?> build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws IOException, InterruptedException {
build.addAction(new MultiSCMRevisionState());
FileOutputStream logStream = new FileOutputStream(changelogFile);
OutputStreamWriter logWriter = new OutputStreamWriter(logStream);
logWriter.write(String.format("<%s>\n", MultiSCMChangeLogParser.ROOT_XML_TAG));
boolean checkoutOK = true;
for (SCM scm : scms) {
String changeLogPath = changelogFile.getPath() + ".temp";
File subChangeLog = new File(changeLogPath);
checkoutOK = scm.checkout(build, launcher, workspace, listener, subChangeLog) && checkoutOK;
String subLogText = FileUtils.readFileToString(subChangeLog);
logWriter.write(String.format("<%s scm=\"%s\">\n<![CDATA[%s]]>\n</%s>\n", MultiSCMChangeLogParser.SUB_LOG_TAG, scm.getClass().getName(), subLogText, MultiSCMChangeLogParser.SUB_LOG_TAG));
subChangeLog.delete();
}
logWriter.write(String.format("</%s>\n", MultiSCMChangeLogParser.ROOT_XML_TAG));
logWriter.close();
return checkoutOK;
}

@Override public ChangeLogParser createChangeLogParser() {
return new MultiSCMChangeLogParser(scms);
}
}

0 comments on commit 022276b

Please sign in to comment.