Skip to content
Permalink
Browse files

JENKINS-25886: Job config and build data selection:

Refactoring and implemented job configuration check to ensure explicit
named remotes if using MultiScm plugin.

Refactoring and improvements:

- Safer and simpler selection of build data, so dublicates are accepted
  if identical, ambiguity is avoided and reported as failure
- Refactoring methods changed interfaces, and is fixed.
- Unit tests is updated.
- Functional tests updated to match new error messages (no logic change)
- One functional tests updated to new use case, it tested now invalid
  configuration.
- Documentation notes are added to some functions.
- UI help text updated with "no trailing slash"-tip
- Functional tests now have headers explaining what is tested.
  • Loading branch information...
MadsNielsen authored and buep committed Dec 3, 2014
1 parent 2a5bdc6 commit 703a6ca9f17a2e02b268306ac7a69a936841ae8e
Showing with 968 additions and 361 deletions.
  1. +8 −8 src/main/java/org/jenkinsci/plugins/pretestedintegration/AbstractSCMBridge.java
  2. +3 −2 src/main/java/org/jenkinsci/plugins/pretestedintegration/IntegrationStrategy.java
  3. +13 −15 src/main/java/org/jenkinsci/plugins/pretestedintegration/PretestedIntegrationAction.java
  4. +15 −31 src/main/java/org/jenkinsci/plugins/pretestedintegration/PretestedIntegrationBuildWrapper.java
  5. +4 −0 src/main/java/org/jenkinsci/plugins/pretestedintegration/exceptions/NextCommitFailureException.java
  6. +3 −1 ...java/org/jenkinsci/plugins/pretestedintegration/exceptions/UnsupportedConfigurationException.java
  7. +10 −8 src/main/java/org/jenkinsci/plugins/pretestedintegration/scm/git/AccumulatedCommitStrategy.java
  8. +117 −119 src/main/java/org/jenkinsci/plugins/pretestedintegration/scm/git/GitBridge.java
  9. +7 −5 src/main/java/org/jenkinsci/plugins/pretestedintegration/scm/git/SquashCommitStrategy.java
  10. +1 −1 src/main/resources/org/jenkinsci/plugins/pretestedintegration/scm/git/GitBridge/help-branch.html
  11. +1 −0 src/main/resources/org/jenkinsci/plugins/pretestedintegration/scm/git/GitBridge/help-repoName.html
  12. +99 −11 ...a/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/AccumulatedCommitStrategyIT.java
  13. +104 −0 ...nkinsci/plugins/pretestedintegration/integration/scm/git/DoNotAllowMasterBranchAsReadyBranch.java
  14. +125 −43 src/test/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/GeneralBehaviourIT.java
  15. +184 −53 src/test/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/JENKINS_24754_IT.java
  16. +15 −16 src/test/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/JENKINS_25546_IT.java
  17. +167 −0 src/test/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/MultipleScmIT.java
  18. +5 −4 ...t/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/SquashCommitStrategyIT.java
  19. +7 −2 src/test/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/TestUtilsFactory.java
  20. +8 −8 src/test/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/TwoBranchHeadsIT.java
  21. +4 −4 ...st/java/org/jenkinsci/plugins/pretestedintegration/integration/scm/git/UniqueBranchGenerator.java
  22. +15 −5 src/test/java/org/jenkinsci/plugins/pretestedintegration/unit/AbstractSCMBridgeTest.java
  23. +12 −2 src/test/java/org/jenkinsci/plugins/pretestedintegration/unit/CommitTest.java
  24. +0 −1 src/test/java/org/jenkinsci/plugins/pretestedintegration/unit/DummyCommit.java
  25. +28 −0 src/test/java/org/jenkinsci/plugins/pretestedintegration/unit/DummyIntegrationStrategy.java
  26. +13 −22 src/test/java/org/jenkinsci/plugins/pretestedintegration/unit/PretestedIntegrationActionTest.java
@@ -86,9 +86,9 @@ public String getBranch() {
* @throws EstablishWorkspaceException
* @throws NothingToDoException
*/
public void prepareWorkspace(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener, Commit<?> commit) throws EstablishWorkspaceException, NothingToDoException, IntegationFailedExeception {
logger.entering("AbstractSCMBridge", "prepareWorkspace", new Object[] { build, listener, launcher, commit });// Generated code DONT TOUCH! Bookmark: 153879db1346111ba452f21bdef681e3
mergeChanges(build, launcher, listener, commit);
public void prepareWorkspace(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws EstablishWorkspaceException, NothingToDoException, IntegationFailedExeception, UnsupportedConfigurationException {
logger.entering("AbstractSCMBridge", "prepareWorkspace", new Object[] { build, listener, launcher });// Generated code DONT TOUCH! Bookmark: 153879db1346111ba452f21bdef681e3
mergeChanges(build, launcher, listener);
logger.exiting("AbstractSCMBridge", "prepareWorkspace");// Generated code DONT TOUCH! Bookmark: c1434f914c8ce73e41e5ea7b7ea1029a
}

@@ -101,9 +101,9 @@ public void prepareWorkspace(AbstractBuild<?, ?> build, Launcher launcher, Build
* @throws NothingToDoException
* @throws IntegationFailedExeception
*/
protected void mergeChanges(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener, Commit<?> commit) throws NothingToDoException, IntegationFailedExeception {
logger.entering("AbstractSCMBridge", "mergeChanges", new Object[] { build, listener, launcher, commit });// Generated code DONT TOUCH! Bookmark: 2929a792ffc98c3546ea6e7755f81916
integrationStrategy.integrate(build, launcher, listener, this, commit);
protected void mergeChanges(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws NothingToDoException, IntegationFailedExeception, UnsupportedConfigurationException {
logger.entering("AbstractSCMBridge", "mergeChanges", new Object[] { build, listener, launcher });// Generated code DONT TOUCH! Bookmark: 2929a792ffc98c3546ea6e7755f81916
integrationStrategy.integrate(build, launcher, listener, this);
logger.exiting("AbstractSCMBridge", "mergeChanges");// Generated code DONT TOUCH! Bookmark: a55866afd1caa8a8ab61b77dbbc2f130
}

@@ -153,12 +153,12 @@ public void commit(AbstractBuild<?, ?> build, Launcher launcher, BuildListener l
logger.exiting("AbstractSCMBridge", "commit");// Generated code DONT TOUCH! Bookmark: 88b215c6f16b1aa5e6458f4686fef503
}

public void deleteIntegratedBranch(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws DeleteIntegratedBranchException {
public void deleteIntegratedBranch(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws DeleteIntegratedBranchException, NothingToDoException, UnsupportedConfigurationException {
logger.entering("AbstractSCMBridge", "deleteIntegratedBranch", new Object[] { build, listener, launcher });// Generated code DONT TOUCH! Bookmark: a499b16a8f9e177b3cb5bb52f9db446b
logger.exiting("AbstractSCMBridge", "deleteIntegratedBranch");// Generated code DONT TOUCH! Bookmark: ec2017cf2a7526cfb7470217ea25413e
}

public void updateBuildDescription(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) {
public void updateBuildDescription(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws NothingToDoException, UnsupportedConfigurationException {
logger.entering("AbstractSCMBridge", "updateBuildDescription", new Object[] { build, listener, launcher });// Generated code DONT TOUCH! Bookmark: c0623c03e84a75c85d9db68bb8078a54
logger.exiting("AbstractSCMBridge", "updateBuildDescription");// Generated code DONT TOUCH! Bookmark: 832f1d788154dbccfc795d2762c455ca
}
@@ -17,12 +17,13 @@
import hudson.model.Descriptor;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.pretestedintegration.exceptions.UnsupportedConfigurationException;
import org.kohsuke.stapler.DataBoundConstructor;

public abstract class IntegrationStrategy implements Describable<IntegrationStrategy>, ExtensionPoint {
private final static Logger logger = Logger.getLogger(IntegrationStrategy.class.getName());// Generated code DONT TOUCH! Bookmark: 3ca61d8e671737b5ead8aaccd31875c4
private final static Logger logger = Logger.getLogger(IntegrationStrategy.class.getName());

public abstract void integrate(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener, AbstractSCMBridge bridge, Commit<?> commit) throws IntegationFailedExeception, NothingToDoException;
public abstract void integrate(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener, AbstractSCMBridge bridge) throws IntegationFailedExeception, NothingToDoException, UnsupportedConfigurationException;

@DataBoundConstructor
public IntegrationStrategy() { }
@@ -11,15 +11,18 @@
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.AbstractBuild;
import org.jenkinsci.plugins.pretestedintegration.exceptions.UnsupportedConfigurationException;

public class PretestedIntegrationAction implements Action {

AbstractBuild<?, ?> build;
AbstractSCMBridge scmBridge;
Commit<?> last;
Commit<?> commit;

private Commit<?> currentIntegrationTip;

@Deprecated
Commit<?> commit;

public PretestedIntegrationAction(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener, AbstractSCMBridge scmBridge) throws NextCommitFailureException {
this.build = build;
this.scmBridge = scmBridge;
@@ -29,24 +32,21 @@ public PretestedIntegrationAction(AbstractBuild<?, ?> build, Launcher launcher,
} catch (NullPointerException e) {
last = null;
}
this.commit = scmBridge.nextCommit(build, launcher, listener, last);
//this.commit = scmBridge.nextCommit(build, launcher, listener, last);
}

@Override
public String getDisplayName() {
logger.entering("PretestedIntegrationAction", "getDisplayName");// Generated code DONT TOUCH! Bookmark: 3fb34e2a852b5aabe47573d3958d0bf0
logger.exiting("PretestedIntegrationAction", "getDisplayName");// Generated code DONT TOUCH! Bookmark: 152a7955fe370131e8203e4d8bfc6c24
return null;
}

public String getIconFileName() {
logger.entering("PretestedIntegrationAction", "getIconFileName");// Generated code DONT TOUCH! Bookmark: ca4d6f91bca42ec63e5f7623655ad110
logger.exiting("PretestedIntegrationAction", "getIconFileName");// Generated code DONT TOUCH! Bookmark: 7b30fa75160a837a01fcf455fdaa57c8
@Override
public String getIconFileName() {
return null;
}

@Override
public String getUrlName() {
logger.entering("PretestedIntegrationAction", "getUrlName");// Generated code DONT TOUCH! Bookmark: cf7c72a753adf447c5674dbc81b3b1b7
logger.exiting("PretestedIntegrationAction", "getUrlName");// Generated code DONT TOUCH! Bookmark: a61b5775071f223c0c2dc704582be204
return "pretested-integration";
}

@@ -68,14 +68,12 @@ public String getUrlName() {
* @throws NothingToDoException
* @throws EstablishWorkspaceException
*/
public boolean initialise(Launcher launcher, BuildListener listener) throws IntegationFailedExeception, NothingToDoException, EstablishWorkspaceException {
public boolean initialise(Launcher launcher, BuildListener listener) throws IntegationFailedExeception, NothingToDoException, EstablishWorkspaceException, UnsupportedConfigurationException {
logger.entering("PretestedIntegrationAction", "initialise", new Object[] { listener, launcher });// Generated code DONT TOUCH! Bookmark: 243ef9e5f61005fcf1963a350f7abb77
boolean result = false;

scmBridge.prepareWorkspace(build, launcher, listener);

if (commit != null) {
result = true;
scmBridge.prepareWorkspace(build, launcher, listener, commit);
}
logger.exiting("PretestedIntegrationAction", "initialise");// Generated code DONT TOUCH! Bookmark: 6f58d37470766bd11e40a451648336e5
return result;
}
@@ -31,38 +31,15 @@

public static final String LOG_PREFIX = "[PREINT] ";
public final AbstractSCMBridge scmBridge;

@Deprecated
private final boolean rollbackEnabled = false;

@DataBoundConstructor
public PretestedIntegrationBuildWrapper(final AbstractSCMBridge scmBridge) {
this.scmBridge = scmBridge;
}

/**
* Goes through the list of builds..finds the latest build which was a pre-test integration.
* @param build
* @return
*/
private AbstractBuild<?,?> findLatestBuildWithPreTestedIntegrationAction(AbstractBuild<?,?> build) {
logger.entering("PretestedIntegrationBuildWrapper", "findLatestBuildWithPreTestedIntegrationAction", new Object[] { build });// Generated code DONT TOUCH! Bookmark: e5a6737aaf1716293a86f1dc6a63f4e2
AbstractBuild<?,?> start = build.getPreviousBuild();
for(AbstractBuild<?,?> i = start; i != null; i = i.getNextBuild()) {
//If the previous build was not pre-test enabled, take next
if(i.getAction(PretestedIntegrationAction.class) == null) {
continue;
}

//if the build is pre-test. Then we only return non-null in case the build was failed.
if(i.getResult().isWorseThan(scmBridge.getRequiredResult())) {
return i;
} else {
return null;
}
}
logger.exiting("PretestedIntegrationBuildWrapper", "findLatestBuildWithPreTestedIntegrationAction");// Generated code DONT TOUCH! Bookmark: e6eabc19c589cecb05c17fa1995117b1
return null;
}

/**
* Jenkins hook that fires after the workspace is initialized. Calls the
* SCM-specific function according to the chosen SCM.
@@ -80,10 +57,20 @@ public PretestedIntegrationBuildWrapper(final AbstractSCMBridge scmBridge) {
boolean proceedToBuildStep = true;
BuildQueue.getInstance().enqueueAndWait();
PretestedIntegrationAction action;
try {
try {
// Check job configuration - there are typically requirements
// on how job is configured before we can allow integration.
scmBridge.validateConfiguration(build.getProject());
// isApplicable basically checks the changeset we git from SCM
// can be used to configure a workspace.
// This is where we check for contraints on what to integrate,
// if there is anything to integrate, if there is ambiguiuty in
// what to integrate.
scmBridge.isApplicable(build, listener);

//Updates workspace to the integration branch
scmBridge.ensureBranch(build, launcher, listener, scmBridge.getBranch());

//Create the action. Record the state of integration branch
action = new PretestedIntegrationAction(build, launcher, listener, scmBridge);
build.addAction(action);
@@ -154,20 +141,17 @@ public void preCheckout() throws IOException, InterruptedException {

@Override
public DescriptorImpl getDescriptor() {
logger.entering("PretestedIntegrationBuildWrapper", "getDescriptor");// Generated code DONT TOUCH! Bookmark: a04a866281166644880e76e2f6650a77
logger.exiting("PretestedIntegrationBuildWrapper", "getDescriptor");// Generated code DONT TOUCH! Bookmark: c05050f6ec75bbdeaa711eded25307bd
return (DescriptorImpl) super.getDescriptor();
}

@Extension
public static class DescriptorImpl extends BuildWrapperDescriptor {

private final static Logger logger = Logger.getLogger(DescriptorImpl.class.getName());// Generated code DONT TOUCH! Bookmark: 3ca61d8e671737b5ead8aaccd31875c4


public DescriptorImpl() {
load();
}

@Override
public String getDisplayName() {
return "Use pretested integration";
}
@@ -17,6 +17,10 @@ public NextCommitFailureException() {
super("Failed to get the next commit to integrate");
}

public NextCommitFailureException(String message) {
super("Failed to get the next commit to integrate: "+message);
}

public NextCommitFailureException(Exception ex) {
super("Failed to get the next commit to integrate", ex);
}
@@ -15,7 +15,9 @@

public static final String ILLEGAL_CONFIG_NO_REPO_NAME_DEFINED = String.format("You have multiple git repositories defined, but none of them matches your pretested integration repostiory. If using more than 1 repository, remotes must be explicitly named in the Git configuration");
public static final String AMBIGUIUTY_IN_REMOTE_NAMES = "You have multiple git repositories defined, and more than one have the same name (or defaults to the same name). Pretested Integration is unable to select the correct one.";

public static final String MULTISCM_REQUIRE_EXPLICIT_NAMING = "You have not explicitly named all your repositories. When using Multiple SCM with Git SCM, we require that all repositories are named. Fill out the 'Name' field for all your repositories";
public static final String AMBIGUIUITY_IN_BUILD_DATA = "Multiple revisions with same remote detected. Cannot determine which one to use.";

public UnsupportedConfigurationException(String message) {
super(message);
}
@@ -7,6 +7,7 @@
package org.jenkinsci.plugins.pretestedintegration.scm.git;

import hudson.Extension;
import hudson.Functions;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
@@ -24,6 +25,7 @@
import org.jenkinsci.plugins.pretestedintegration.IntegrationStrategy;
import org.jenkinsci.plugins.pretestedintegration.IntegrationStrategyDescriptor;
import org.jenkinsci.plugins.pretestedintegration.exceptions.NothingToDoException;
import org.jenkinsci.plugins.pretestedintegration.exceptions.UnsupportedConfigurationException;
import org.kohsuke.stapler.DataBoundConstructor;

/**
@@ -39,16 +41,17 @@
public AccumulatedCommitStrategy() { }

@Override
public void integrate(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener, AbstractSCMBridge bridge, Commit<?> commit) throws IntegationFailedExeception, NothingToDoException {
logger.entering("AccumulatedCommitStrategy", "integrate", new Object[] { build, listener, bridge, launcher, commit });// Generated code DONT TOUCH! Bookmark: ee74dbf7df6fa51582ccc15f5fee72da
public void integrate(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener, AbstractSCMBridge bridge) throws IntegationFailedExeception, NothingToDoException, UnsupportedConfigurationException {
logger.entering("AccumulatedCommitStrategy", "integrate", new Object[] { build, listener, bridge, launcher });// Generated code DONT TOUCH! Bookmark: ee74dbf7df6fa51582ccc15f5fee72da
int exitCode = -999;

GitClient client;

GitBridge gitbridge = (GitBridge)bridge;

ByteArrayOutputStream out = new ByteArrayOutputStream();
BuildData gitBuildData = build.getAction(BuildData.class);
BuildData gitBuildData = gitbridge.checkAndDetermineRelevantBuildData(build.getActions(BuildData.class));
String commit = gitBuildData.lastBuild.revision.getSha1String();

//TODO: Implement robustness, in which situations does this one contain multiple revisons, when two branches point to the same commit? (JENKINS-24909). Check branch spec before doing anything
Branch gitDataBranch = gitBuildData.lastBuild.revision.getBranches().iterator().next();
@@ -58,9 +61,8 @@ public void integrate(AbstractBuild<?,?> build, Launcher launcher, BuildListener
client = Git.with(listener, build.getEnvironment(listener)).in(gitbridge.resolveWorkspace(build, listener)).getClient();
logger.fine("Finding remote branches");
for(Branch b : client.getRemoteBranches()) {
logger.fine(String.format("Found remote branch %s", b.getName()));

if(b.getName().equals(gitDataBranch.getName())) {
logger.fine(String.format("Found remote branch %s", b.getName()));
if(b.getSHA1String().equals(gitDataBranch.getSHA1String())) {
found = true;
break;
}
@@ -82,11 +84,11 @@ public void integrate(AbstractBuild<?,?> build, Launcher launcher, BuildListener
throw new NothingToDoException(String.format("The branch name (%s) used by git, did not match a remote branch name. You might already have integrated the branch", gitDataBranch != null ? gitDataBranch.getName() : "null"));
}

listener.getLogger().println( String.format( "Preparing to merge changes in commit %s to integration branch %s", (String) commit.getId(), gitbridge.getBranch() ) );
listener.getLogger().println( String.format( "Preparing to merge changes in commit %s to integration branch %s", commit, gitbridge.getBranch() ) );
try {

String commitMessage = client.withRepository(new FindCommitMessageCallback(listener, gitDataBranch.getSHA1()));
exitCode = gitbridge.git(build, launcher, listener, out, "merge","-m", String.format("%s\n[%s]", commitMessage, gitDataBranch.getName()), (String) commit.getId(), "--no-ff");
exitCode = gitbridge.git(build, launcher, listener, out, "merge","-m", String.format("%s\n[%s]", commitMessage, gitDataBranch.getName()), commit, "--no-ff");
} catch (Exception ex) {
logger.exiting("AccumulatedCommitStrategy ", "integrate-mergeFailure");// Generated code DONT TOUCH! Bookmark: 26b6ce59c6edbad7afa29f96febc6fd7
logger.log(Level.WARNING, "Exception while merging, logging exception",ex);

0 comments on commit 703a6ca

Please sign in to comment.
You can’t perform that action at this time.