Created
July 2, 2010 23:25
-
-
Save ieure/462074 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:615: incompatible types | |
found : java.util.Collection<hudson.plugins.git.Revision> | |
required: hudson.plugins.git.Revision | |
return candidates; | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:542: incompatible types | |
found : hudson.plugins.git.Revision | |
required: java.util.Collection<hudson.plugins.git.Revision> | |
final Collection<Revision> revsToBuild = workspace.act(new FileCallable<Revision>() { | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:626: cannot find symbol | |
symbol : variable revToBuild | |
location: class hudson.plugins.git.GitSCM | |
listener.getLogger().println("Commencing build of " + revToBuild); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:627: cannot find symbol | |
symbol : variable revToBuild | |
location: class hudson.plugins.git.GitSCM | |
environment.put(GIT_COMMIT, revToBuild.getSha1String()); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:647: local variable revToBuild is accessed from within inner class; needs to be declared final | |
"Merging " + revToBuild + " onto " | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:655: local variable revToBuild is accessed from within inner class; needs to be declared final | |
git.merge(revToBuild.getSha1().name()); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:666: local variable revToBuild is accessed from within inner class; needs to be declared final | |
git.checkout(revToBuild.getSha1().name()); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:672: local variable revToBuild is accessed from within inner class; needs to be declared final | |
buildData.saveBuild(new Build(revToBuild, buildNumber, Result.FAILURE)); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:686: local variable revToBuild is accessed from within inner class; needs to be declared final | |
if(revToBuild.getBranches().size() > 0) | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:691: local variable revToBuild is accessed from within inner class; needs to be declared final | |
for(Branch b : revToBuild.getBranches()) { | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:695: local variable revToBuild is accessed from within inner class; needs to be declared final | |
changeLog.append(putChangelogDiffsIntoFile(git, b.name, lastRevWas.getSHA1().name(), revToBuild.getSha1().name())); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:702: local variable revToBuild is accessed from within inner class; needs to be declared final | |
Build build = new Build(revToBuild, buildNumber, null); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:720: incompatible types | |
found : hudson.plugins.git.Revision | |
required: java.util.Collection<hudson.plugins.git.Revision> | |
final Collection<Revision> revToBuild = revsToBuild.iterator().next(); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:730: cannot find symbol | |
symbol : method getSha1() | |
location: interface java.util.Collection<hudson.plugins.git.Revision> | |
git.checkout(revToBuild.getSha1().name()); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:769: cannot find symbol | |
symbol : method getBranches() | |
location: interface java.util.Collection<hudson.plugins.git.Revision> | |
for(Branch b : revToBuild.getBranches()) { | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:774: cannot find symbol | |
symbol : method getSha1() | |
location: interface java.util.Collection<hudson.plugins.git.Revision> | |
changeLog.append(putChangelogDiffsIntoFile(git, b.name, lastRevWas.getSHA1().name(), revToBuild.getSha1().name())); | |
^ | |
/Users/ieure/Projects/Hudson-GIT-plugin/src/main/java/hudson/plugins/git/GitSCM.java:788: cannot find symbol | |
symbol : constructor Build(java.util.Collection<hudson.plugins.git.Revision>,int,<nulltype>) | |
location: class hudson.plugins.git.util.Build | |
buildData.saveBuild(new Build(revToBuild, buildNumber, null)); | |
^ | |
17 errors | |
[INFO] ------------------------------------------------------------------------ | |
[ERROR] BUILD ERROR | |
[INFO] ------------------------------------------------------------------------ | |
[INFO] Fatal error compiling |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package hudson.plugins.git; | |
import hudson.*; | |
import hudson.FilePath.FileCallable; | |
import hudson.matrix.MatrixBuild; | |
import hudson.matrix.MatrixRun; | |
import hudson.model.*; | |
import hudson.plugins.git.browser.GitWeb; | |
import hudson.plugins.git.browser.GithubWeb; | |
import hudson.plugins.git.opt.PreBuildMergeOptions; | |
import hudson.plugins.git.util.*; | |
import hudson.plugins.git.util.Build; | |
import hudson.remoting.VirtualChannel; | |
import hudson.scm.ChangeLogParser; | |
import hudson.scm.RepositoryBrowser; | |
import hudson.scm.RepositoryBrowsers; | |
import hudson.scm.SCM; | |
import hudson.scm.SCMDescriptor; | |
import hudson.util.FormValidation; | |
import java.io.ByteArrayOutputStream; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.io.Serializable; | |
import java.net.MalformedURLException; | |
import java.util.*; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
import javax.servlet.ServletException; | |
import net.sf.json.JSONObject; | |
import org.kohsuke.stapler.DataBoundConstructor; | |
import org.kohsuke.stapler.StaplerRequest; | |
import org.kohsuke.stapler.StaplerResponse; | |
import org.spearce.jgit.lib.ObjectId; | |
import org.spearce.jgit.lib.RepositoryConfig; | |
import org.spearce.jgit.transport.RefSpec; | |
import org.spearce.jgit.transport.RemoteConfig; | |
/** | |
* Git SCM. | |
* | |
* @author Nigel Magnay | |
*/ | |
public class GitSCM extends SCM implements Serializable { | |
// old fields are left so that old config data can be read in, but | |
// they are deprecated. transient so that they won't show up in XML | |
// when writing back | |
@Deprecated transient String source; | |
@Deprecated transient String branch; | |
/** | |
* Store a config version so we're able to migrate config on various | |
* functionality upgrades. | |
*/ | |
private Long configVersion; | |
/** | |
* All the remote repositories that we know about. | |
*/ | |
private List<RemoteConfig> remoteRepositories; | |
/** | |
* All the branches that we wish to care about building. | |
*/ | |
private List<BranchSpec> branches; | |
/** | |
* Options for merging before a build. | |
*/ | |
private PreBuildMergeOptions mergeOptions; | |
private boolean doGenerateSubmoduleConfigurations; | |
private boolean authorOrCommitter; | |
private boolean clean; | |
private boolean wipeOutWorkspace; | |
/** | |
* @deprecated | |
* Replaced by {@link #buildChooser} instead. | |
*/ | |
private transient String choosingStrategy; | |
private BuildChooser buildChooser; | |
public String gitTool = null; | |
private GitRepositoryBrowser browser; | |
private Collection<SubmoduleConfig> submoduleCfg; | |
public static final String GIT_BRANCH = "GIT_BRANCH"; | |
public static final String GIT_COMMIT = "GIT_COMMIT"; | |
public Collection<SubmoduleConfig> getSubmoduleCfg() { | |
return submoduleCfg; | |
} | |
public void setSubmoduleCfg(Collection<SubmoduleConfig> submoduleCfg) { | |
this.submoduleCfg = submoduleCfg; | |
} | |
@DataBoundConstructor | |
public GitSCM( | |
List<RemoteConfig> repositories, | |
List<BranchSpec> branches, | |
PreBuildMergeOptions mergeOptions, | |
boolean doGenerateSubmoduleConfigurations, | |
Collection<SubmoduleConfig> submoduleCfg, | |
boolean clean, | |
boolean wipeOutWorkspace, | |
BuildChooser buildChooser, GitRepositoryBrowser browser, | |
String gitTool, | |
boolean authorOrCommitter) { | |
// normalization | |
this.branches = branches; | |
this.remoteRepositories = repositories; | |
this.browser = browser; | |
this.mergeOptions = mergeOptions; | |
this.doGenerateSubmoduleConfigurations = doGenerateSubmoduleConfigurations; | |
this.submoduleCfg = submoduleCfg; | |
this.clean = clean; | |
this.wipeOutWorkspace = wipeOutWorkspace; | |
this.configVersion = 1L; | |
this.gitTool = gitTool; | |
this.authorOrCommitter = authorOrCommitter; | |
this.buildChooser = buildChooser; | |
buildChooser.gitSCM = this; // set the owner | |
} | |
public Object readResolve() { | |
// Migrate data | |
// Default unspecified to v0 | |
if(configVersion == null) | |
configVersion = 0L; | |
if(source!=null) { | |
remoteRepositories = new ArrayList<RemoteConfig>(); | |
branches = new ArrayList<BranchSpec>(); | |
doGenerateSubmoduleConfigurations = false; | |
mergeOptions = new PreBuildMergeOptions(); | |
remoteRepositories.add(newRemoteConfig("origin", source, new RefSpec("+refs/heads/*:refs/remotes/origin/*"))); | |
if(branch != null) { | |
branches.add(new BranchSpec(branch)); | |
} | |
else { | |
branches.add(new BranchSpec("*/master")); | |
} | |
} | |
if(configVersion < 1 && branches != null) { | |
// Migrate the branch specs from | |
// single * wildcard, to ** wildcard. | |
for(BranchSpec branchSpec : branches) { | |
String name = branchSpec.getName(); | |
name = name.replace("*", "**"); | |
branchSpec.setName(name); | |
} | |
} | |
if(mergeOptions.doMerge() && mergeOptions.getMergeRemote() == null) { | |
mergeOptions.setMergeRemote(remoteRepositories.get(0)); | |
} | |
if (choosingStrategy!=null && buildChooser==null) { | |
for (BuildChooserDescriptor d : BuildChooser.all()) { | |
if (choosingStrategy.equals(d.getLegacyId())) | |
try { | |
buildChooser = d.clazz.newInstance(); | |
} catch (InstantiationException e) { | |
LOGGER.log(Level.WARNING, "Failed to instantiate the build chooser",e); | |
} catch (IllegalAccessException e) { | |
LOGGER.log(Level.WARNING, "Failed to instantiate the build chooser",e); | |
} | |
} | |
} | |
if (buildChooser==null) | |
buildChooser = new DefaultBuildChooser(); | |
buildChooser.gitSCM = this; | |
return this; | |
} | |
@Override | |
public GitRepositoryBrowser getBrowser() { | |
return browser; | |
} | |
public boolean getWipeOutWorkspace() { | |
return this.wipeOutWorkspace; | |
} | |
public boolean getClean() { | |
return this.clean; | |
} | |
public BuildChooser getBuildChooser() { | |
return buildChooser; | |
} | |
public List<RemoteConfig> getParamExpandedRepos(AbstractBuild<?,?> build) { | |
if (remoteRepositories == null) | |
return new ArrayList<RemoteConfig>(); | |
else { | |
List<RemoteConfig> expandedRepos = new ArrayList<RemoteConfig>(); | |
for (RemoteConfig oldRepo : remoteRepositories) { | |
expandedRepos.add(newRemoteConfig(oldRepo.getName(), | |
oldRepo.getURIs().get(0).toString(), | |
new RefSpec(getRefSpec(oldRepo, build)))); | |
} | |
return expandedRepos; | |
} | |
} | |
public List<RemoteConfig> getRepositories() { | |
// Handle null-value to ensure backwards-compatibility, ie project configuration missing the <repositories/> XML element | |
if (remoteRepositories == null) | |
return new ArrayList<RemoteConfig>(); | |
return remoteRepositories; | |
} | |
public String getGitTool() { | |
return gitTool; | |
} | |
private String getRefSpec(RemoteConfig repo, AbstractBuild<?,?> build) { | |
String refSpec = repo.getFetchRefSpecs().get(0).toString(); | |
ParametersAction parameters = build.getAction(ParametersAction.class); | |
if (parameters != null) | |
refSpec = parameters.substitute(build, refSpec); | |
return refSpec; | |
} | |
private String getSingleBranch(AbstractBuild<?, ?> build) { | |
// if we have multiple branches skip to advanced usecase | |
if (getBranches().size() != 1 || getRepositories().size() != 1) | |
return null; | |
String branch = getBranches().get(0).getName(); | |
String repository = getRepositories().get(0).getName(); | |
// replace repository wildcard with repository name | |
if (branch.startsWith("*/")) | |
branch = repository + branch.substring(1); | |
// if the branch name contains more wildcards then the simple usecase | |
// does not apply and we need to skip to the advanced usecase | |
if (branch.contains("*")) | |
return null; | |
// substitute build parameters if available | |
ParametersAction parameters = build.getAction(ParametersAction.class); | |
if (parameters != null) | |
branch = parameters.substitute(build, branch); | |
return branch; | |
} | |
@Override | |
public boolean pollChanges(final AbstractProject project, Launcher launcher, | |
final FilePath workspace, final TaskListener listener) | |
throws IOException, InterruptedException { | |
// Poll for changes. Are there any unbuilt revisions that Hudson ought to build ? | |
listener.getLogger().println("Using strategy: " + buildChooser.getDisplayName()); | |
final AbstractBuild lastBuild = (AbstractBuild)project.getLastBuild(); | |
if(lastBuild != null) { | |
listener.getLogger().println("[poll] Last Build : #" + lastBuild.getNumber()); | |
} | |
final BuildData buildData = fixNull(getBuildData(lastBuild, false)); | |
if(buildData != null && buildData.lastBuild != null) { | |
listener.getLogger().println("[poll] Last Built Revision: " + buildData.lastBuild.revision); | |
} | |
final String singleBranch = getSingleBranch(lastBuild); | |
Label label = project.getAssignedLabel(); | |
final String gitExe; | |
final List<RemoteConfig> paramRepos = getParamExpandedRepos(lastBuild); | |
//If this project is tied onto a node, it's built always there. On other cases, | |
//polling is done on the node which did the last build. | |
// | |
if (label != null && label.isSelfLabel()) { | |
if(label.getNodes().iterator().next() != project.getLastBuiltOn()) { | |
listener.getLogger().println("Last build was not on tied node, forcing rebuild."); | |
return true; | |
} | |
gitExe = getGitExe(label.getNodes().iterator().next(), listener); | |
} else { | |
gitExe = getGitExe(project.getLastBuiltOn(), listener); | |
} | |
boolean pollChangesResult = workspace.act(new FileCallable<Boolean>() { | |
private static final long serialVersionUID = 1L; | |
public Boolean invoke(File localWorkspace, VirtualChannel channel) throws IOException { | |
EnvVars environment = new EnvVars(System.getenv()); | |
IGitAPI git = new GitAPI(gitExe, new FilePath(localWorkspace), listener, environment); | |
if (git.hasGitRepo()) { | |
// Repo is there - do a fetch | |
listener.getLogger().println("Fetching changes from the remote Git repositories"); | |
// Fetch updates | |
for (RemoteConfig remoteRepository : paramRepos) { | |
fetchFrom(git, localWorkspace, listener, remoteRepository); | |
} | |
listener.getLogger().println("Polling for changes in"); | |
Collection<Revision> candidates = buildChooser.getCandidateRevisions( | |
true, singleBranch, git, listener, buildData); | |
return (candidates.size() > 0); | |
} else { | |
listener.getLogger().println("No Git repository yet, an initial checkout is required"); | |
return true; | |
} | |
} | |
}); | |
return pollChangesResult; | |
} | |
private BuildData fixNull(BuildData bd) { | |
return bd!=null ? bd : new BuildData() /*dummy*/; | |
} | |
/** | |
* Fetch information from a particular remote repository. Attempt to fetch | |
* from submodules, if they exist in the local WC | |
* | |
* @param git | |
* @param listener | |
* @param remoteRepository | |
* @throws | |
*/ | |
private void fetchFrom(IGitAPI git, File workspace, TaskListener listener, | |
RemoteConfig remoteRepository) { | |
try { | |
git.fetch(remoteRepository); | |
List<IndexEntry> submodules = new GitUtils(listener, git) | |
.getSubmodules("HEAD"); | |
for (IndexEntry submodule : submodules) { | |
try { | |
RemoteConfig submoduleRemoteRepository = getSubmoduleRepository(remoteRepository, submodule.getFile()); | |
File subdir = new File(workspace, submodule.getFile()); | |
IGitAPI subGit = new GitAPI(git.getGitExe(), new FilePath(subdir), | |
listener, git.getEnvironment()); | |
subGit.fetch(submoduleRemoteRepository); | |
} catch (Exception ex) { | |
listener | |
.error( | |
"Problem fetching from " | |
+ remoteRepository.getName() | |
+ " - could be unavailable. Continuing anyway"); | |
} | |
} | |
} catch (GitException ex) { | |
listener.error( | |
"Problem fetching from " + remoteRepository.getName() | |
+ " / " + remoteRepository.getName() | |
+ " - could be unavailable. Continuing anyway"); | |
} | |
} | |
public RemoteConfig getSubmoduleRepository(RemoteConfig orig, String name) { | |
// Attempt to guess the submodule URL?? | |
String refUrl = orig.getURIs().get(0).toString(); | |
if (refUrl.endsWith("/.git")) { | |
refUrl = refUrl.substring(0, refUrl.length() - 4); | |
} | |
if (!refUrl.endsWith("/")) refUrl += "/"; | |
refUrl += name; | |
if (!refUrl.endsWith("/")) refUrl += "/"; | |
refUrl += ".git"; | |
return newRemoteConfig(name, refUrl, orig.getFetchRefSpecs().get(0)); | |
} | |
private RemoteConfig newRemoteConfig(String name, String refUrl, RefSpec refSpec) { | |
File temp = null; | |
try { | |
temp = File.createTempFile("tmp", "config"); | |
RepositoryConfig repoConfig = new RepositoryConfig(null, temp); | |
// Make up a repo config from the request parameters | |
repoConfig.setString("remote", name, "url", refUrl); | |
repoConfig.setString("remote", name, "fetch", refSpec.toString()); | |
repoConfig.save(); | |
return RemoteConfig.getAllRemoteConfigs(repoConfig).get(0); | |
} | |
catch(Exception ex) { | |
throw new GitException("Error creating temp file"); | |
} | |
finally { | |
if(temp != null) | |
temp.delete(); | |
} | |
} | |
private boolean changeLogResult(String changeLog, File changelogFile) throws IOException { | |
if (changeLog == null) | |
return false; | |
else { | |
changelogFile.delete(); | |
FileOutputStream fos = new FileOutputStream(changelogFile); | |
fos.write(changeLog.getBytes()); | |
fos.close(); | |
// Write to file | |
return true; | |
} | |
} | |
/** | |
* Exposing so that we can get this from GitPublisher. | |
*/ | |
public String getGitExe(Node builtOn, TaskListener listener) { | |
GitTool[] gitToolInstallations = Hudson.getInstance().getDescriptorByType(GitTool.DescriptorImpl.class).getInstallations(); | |
for(GitTool t : gitToolInstallations) { | |
//If gitTool is null, use first one. | |
if(gitTool == null) { | |
gitTool = t.getName(); | |
} | |
if(t.getName().equals(gitTool)) { | |
if(builtOn != null){ | |
try { | |
String s = t.forNode(builtOn, listener).getGitExe(); | |
return s; | |
} catch (IOException e) { | |
listener.getLogger().println("Failed to get git executable"); | |
} catch (InterruptedException e) { | |
listener.getLogger().println("Failed to get git executable"); | |
} | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* If true, use the commit author as the changeset author, rather | |
* than the committer. | |
*/ | |
public boolean getAuthorOrCommitter() { | |
return authorOrCommitter; | |
} | |
@Override | |
public boolean checkout(final AbstractBuild build, Launcher launcher, | |
final FilePath workspace, final BuildListener listener, File changelogFile) | |
throws IOException, InterruptedException { | |
Object[] returnData; // Changelog, BuildData | |
listener.getLogger().println("Checkout:" + workspace.getName() + " / " + workspace.getRemote() + " - " + workspace.getChannel()); | |
listener.getLogger().println("Using strategy: " + buildChooser.getDisplayName()); | |
final String projectName = build.getProject().getName(); | |
final int buildNumber = build.getNumber(); | |
final String gitExe = getGitExe(build.getBuiltOn(), listener); | |
final String buildnumber = "hudson-" + projectName + "-" + buildNumber; | |
final BuildData buildData = getBuildData(build.getPreviousBuild(), true); | |
if(buildData != null && buildData.lastBuild != null) { | |
listener.getLogger().println("Last Built Revision: " + buildData.lastBuild.revision); | |
} | |
final EnvVars environment = build.getEnvironment(listener); | |
final String singleBranch = getSingleBranch(build); | |
Revision tempParentLastBuiltRev = null; | |
if (build instanceof MatrixRun) { | |
MatrixBuild parentBuild = ((MatrixRun)build).getParentBuild(); | |
if (parentBuild != null) { | |
BuildData parentBuildData = parentBuild.getAction(BuildData.class); | |
if (parentBuildData != null) { | |
tempParentLastBuiltRev = parentBuildData.getLastBuiltRevision(); | |
} | |
} | |
} | |
final List<RemoteConfig> paramRepos = getParamExpandedRepos(build); | |
final Revision parentLastBuiltRev = tempParentLastBuiltRev; | |
final Collection<Revision> revsToBuild = workspace.act(new FileCallable<Revision>() { | |
private static final long serialVersionUID = 1L; | |
public Revision invoke(File localWorkspace, VirtualChannel channel) | |
throws IOException { | |
FilePath ws = new FilePath(localWorkspace); | |
listener.getLogger().println("Checkout:" + ws.getName() + " / " + ws.getRemote() + " - " + ws.getChannel()); | |
IGitAPI git = new GitAPI(gitExe, ws, listener, environment); | |
if (wipeOutWorkspace) { | |
listener.getLogger().println("Wiping out workspace first"); | |
try { | |
ws.deleteContents(); | |
} catch (InterruptedException e) { | |
// I don't really care if this fails. | |
} | |
} | |
if (git.hasGitRepo()) { | |
// It's an update | |
listener.getLogger().println("Fetching changes from the remote Git repository"); | |
for (RemoteConfig remoteRepository : paramRepos) { | |
fetchFrom(git,localWorkspace,listener,remoteRepository); | |
} | |
} else { | |
listener.getLogger().println("Cloning the remote Git repository"); | |
// Go through the repositories, trying to clone from one | |
// | |
boolean successfullyCloned = false; | |
for(RemoteConfig rc : paramRepos) { | |
try { | |
git.clone(rc); | |
successfullyCloned = true; | |
break; | |
} | |
catch(GitException ex) { | |
listener.error("Error cloning remote repo '%s' : %s", rc.getName(), ex.getMessage()); | |
if(ex.getCause() != null) { | |
listener.error("Cause: %s", ex.getCause().getMessage()); | |
} | |
// Failed. Try the next one | |
listener.getLogger().println("Trying next repository"); | |
} | |
} | |
if(!successfullyCloned) { | |
listener.error("Could not clone from a repository"); | |
throw new GitException("Could not clone"); | |
} | |
// Also do a fetch | |
for (RemoteConfig remoteRepository : paramRepos) { | |
fetchFrom(git,localWorkspace,listener,remoteRepository); | |
} | |
if (git.hasGitModules()) { | |
git.submoduleInit(); | |
git.submoduleUpdate(); | |
} | |
} | |
if (parentLastBuiltRev != null) | |
return parentLastBuiltRev; | |
Collection<Revision> candidates = buildChooser.getCandidateRevisions( | |
false, singleBranch, git, listener, buildData); | |
if(candidates.size() == 0) | |
return null; | |
return candidates; | |
} | |
}); | |
if(revsToBuild == null) { | |
// getBuildCandidates should make the last item the last build, so a re-build | |
// will build the last built thing. | |
listener.error("Nothing to do"); | |
return false; | |
} | |
listener.getLogger().println("Commencing build of " + revToBuild); | |
environment.put(GIT_COMMIT, revToBuild.getSha1String()); | |
if (revsToBuild.size() > 1 && !mergeOptions.doMerge()) { | |
throw new GitException("Multiple changes to build, but merging is disabled."); | |
} | |
if (mergeOptions.doMerge()) { | |
for (Revision revToBuild : revsToBuild) { | |
if (!revToBuild.containsBranchName(mergeOptions.getRemoteBranchName())) { | |
returnData = workspace.act(new FileCallable<Object[]>() { | |
private static final long serialVersionUID = 1L; | |
public Object[] invoke(File localWorkspace, VirtualChannel channel) | |
throws IOException { | |
IGitAPI git = new GitAPI(gitExe, new FilePath(localWorkspace), listener, environment); | |
// Do we need to merge this revision onto MergeTarget | |
// Only merge if there's a branch to merge that isn't | |
// us.. | |
listener.getLogger().println( | |
"Merging " + revToBuild + " onto " | |
+ mergeOptions.getMergeTarget()); | |
// checkout origin/blah | |
ObjectId target = git.revParse(mergeOptions.getRemoteBranchName()); | |
git.checkout(target.name()); | |
try { | |
git.merge(revToBuild.getSha1().name()); | |
} catch (Exception ex) { | |
listener | |
.getLogger() | |
.println( | |
"Branch not suitable for integration as it does not merge cleanly"); | |
// We still need to tag something to prevent | |
// repetitive builds from happening - tag the | |
// candidate | |
// branch. | |
git.checkout(revToBuild.getSha1().name()); | |
git.tag(buildnumber, "Hudson Build #" | |
+ buildNumber); | |
buildData.saveBuild(new Build(revToBuild, buildNumber, Result.FAILURE)); | |
return new Object[]{null, buildData}; | |
} | |
if (git.hasGitModules()) { | |
git.submoduleUpdate(); | |
} | |
// Tag the successful merge | |
git.tag(buildnumber, "Hudson Build #" + buildNumber); | |
StringBuilder changeLog = new StringBuilder(); | |
if(revToBuild.getBranches().size() > 0) | |
listener.getLogger().println("Warning : There are multiple branch changesets here"); | |
try { | |
for(Branch b : revToBuild.getBranches()) { | |
Build lastRevWas = buildChooser.prevBuildForChangelog(b.getName(), | |
buildData, git); | |
if(lastRevWas != null) { | |
changeLog.append(putChangelogDiffsIntoFile(git, b.name, lastRevWas.getSHA1().name(), revToBuild.getSha1().name())); | |
} | |
} | |
} catch (GitException ge) { | |
changeLog.append("Unable to retrieve changeset"); | |
} | |
Build build = new Build(revToBuild, buildNumber, null); | |
buildData.saveBuild(build); | |
GitUtils gu = new GitUtils(listener,git); | |
build.mergeRevision = gu.getRevisionForSHA1(target); | |
// Fetch the diffs into the changelog file | |
return new Object[]{changeLog.toString(), buildData}; | |
} | |
}); | |
BuildData returningBuildData = (BuildData)returnData[1]; | |
build.addAction(returningBuildData); | |
return changeLogResult((String) returnData[0], changelogFile); | |
} | |
} | |
} | |
// No merge | |
final Collection<Revision> revToBuild = revsToBuild.iterator().next(); | |
returnData = workspace.act(new FileCallable<Object[]>() { | |
private static final long serialVersionUID = 1L; | |
public Object[] invoke(File localWorkspace, VirtualChannel channel) | |
throws IOException { | |
IGitAPI git = new GitAPI(gitExe, new FilePath(localWorkspace), listener, environment); | |
// Straight compile-the-branch | |
listener.getLogger().println("Checking out " + revToBuild); | |
git.checkout(revToBuild.getSha1().name()); | |
// if(compileSubmoduleCompares) | |
if (doGenerateSubmoduleConfigurations) { | |
SubmoduleCombinator combinator = new SubmoduleCombinator( | |
git, listener, localWorkspace, submoduleCfg); | |
combinator.createSubmoduleCombinations(); | |
} | |
if (git.hasGitModules()) { | |
git.submoduleInit(); | |
git.submoduleSync(); | |
// Git submodule update will only 'fetch' from where it | |
// regards as 'origin'. However, | |
// it is possible that we are building from a | |
// RemoteRepository with changes | |
// that are not in 'origin' AND it may be a new module that | |
// we've only just discovered. | |
// So - try updating from all RRs, then use the submodule | |
// Update to do the checkout | |
for (RemoteConfig remoteRepository : paramRepos) { | |
fetchFrom(git, localWorkspace, listener, remoteRepository); | |
} | |
// Update to the correct checkout | |
git.submoduleUpdate(); | |
} | |
// Tag the successful merge | |
git.tag(buildnumber, "Hudson Build #" + buildNumber); | |
StringBuilder changeLog = new StringBuilder(); | |
int histories = 0; | |
try { | |
for(Branch b : revToBuild.getBranches()) { | |
Build lastRevWas = buildChooser.prevBuildForChangelog(b.getName(), buildData, git); | |
if(lastRevWas != null) { | |
listener.getLogger().println("Recording changes in branch " + b.getName()); | |
changeLog.append(putChangelogDiffsIntoFile(git, b.name, lastRevWas.getSHA1().name(), revToBuild.getSha1().name())); | |
histories++; | |
} else { | |
listener.getLogger().println("No change to record in branch " + b.getName()); | |
} | |
} | |
} catch (GitException ge) { | |
changeLog.append("Unable to retrieve changeset"); | |
} | |
if(histories > 1) | |
listener.getLogger().println("Warning : There are multiple branch changesets here"); | |
buildData.saveBuild(new Build(revToBuild, buildNumber, null)); | |
if (getClean()) { | |
listener.getLogger().println("Cleaning workspace"); | |
git.clean(); | |
} | |
// Fetch the diffs into the changelog file | |
return new Object[]{changeLog.toString(), buildData}; | |
} | |
}); | |
build.addAction((Action) returnData[1]); | |
return changeLogResult((String) returnData[0], changelogFile); | |
} | |
public void buildEnvVars(AbstractBuild build, java.util.Map<String, String> env) { | |
super.buildEnvVars(build, env); | |
String branch = getSingleBranch(build); | |
if(branch != null){ | |
env.put(GIT_BRANCH, branch); | |
} | |
} | |
private String putChangelogDiffsIntoFile(IGitAPI git, String branchName, String revFrom, | |
String revTo) throws IOException { | |
ByteArrayOutputStream fos = new ByteArrayOutputStream(); | |
// fos.write("<data><![CDATA[".getBytes()); | |
String changeset = "Changes in branch " + branchName + ", between " + revFrom + " and " + revTo + "\n"; | |
fos.write(changeset.getBytes()); | |
git.changelog(revFrom, revTo, fos); | |
// fos.write("]]></data>".getBytes()); | |
fos.close(); | |
return fos.toString(); | |
} | |
@Override | |
public ChangeLogParser createChangeLogParser() { | |
return new GitChangeLogParser(getAuthorOrCommitter()); | |
} | |
@Extension | |
public static final class DescriptorImpl extends SCMDescriptor<GitSCM> { | |
private String gitExe; | |
public DescriptorImpl() { | |
super(GitSCM.class, GitRepositoryBrowser.class); | |
load(); | |
} | |
public String getDisplayName() { | |
return "Git"; | |
} | |
public List<BuildChooserDescriptor> getBuildChooserDescriptors() { | |
return BuildChooser.all(); | |
} | |
/** | |
* Lists available toolinstallations. | |
* @return list of available git tools | |
*/ | |
public List<GitTool> getGitTools() { | |
GitTool[] gitToolInstallations = Hudson.getInstance().getDescriptorByType(GitTool.DescriptorImpl.class).getInstallations(); | |
return Arrays.asList(gitToolInstallations); | |
} | |
/** | |
* Path to git executable. | |
* @deprecated | |
* @see GitTool | |
*/ | |
@Deprecated | |
public String getGitExe() { | |
return gitExe; | |
} | |
/** | |
* Old configuration of git executable - exposed so that we can | |
* migrate this setting to GitTool without deprecation warnings. | |
*/ | |
public String getOldGitExe() { | |
return gitExe; | |
} | |
public SCM newInstance(StaplerRequest req, JSONObject formData) throws FormException { | |
List<RemoteConfig> remoteRepositories; | |
File temp = null; | |
try { | |
temp = File.createTempFile("tmp", "config"); | |
remoteRepositories = createRepositoryConfigurations(req.getParameterValues("git.repo.url"), | |
req.getParameterValues("git.repo.name"), req.getParameterValues("git.repo.refspec"), | |
temp); | |
} | |
catch (IOException e1) { | |
throw new GitException("Error creating repositories", e1); | |
} | |
finally { | |
if(temp != null) { | |
temp.delete(); | |
} | |
} | |
List<BranchSpec> branches = createBranches(req.getParameterValues("git.branch")); | |
// Make up a repo config from the request parameters | |
PreBuildMergeOptions mergeOptions = createMergeOptions(req.getParameter("git.doMerge"), | |
req.getParameter("git.mergeRemote"), req.getParameter("git.mergeTarget"), | |
remoteRepositories); | |
String[] urls = req.getParameterValues("git.repo.url"); | |
String[] names = req.getParameterValues("git.repo.name"); | |
Collection<SubmoduleConfig> submoduleCfg = new ArrayList<SubmoduleConfig>(); | |
final GitRepositoryBrowser gitBrowser = getBrowserFromRequest(req); | |
String gitTool = req.getParameter("git.gitTool"); | |
return new GitSCM( | |
remoteRepositories, | |
branches, | |
mergeOptions, | |
req.getParameter("git.generate") != null, | |
submoduleCfg, | |
req.getParameter("git.clean") != null, | |
req.getParameter("git.wipeOutWorkspace") != null, | |
req.bindJSON(BuildChooser.class,formData.getJSONObject("buildChooser")), | |
gitBrowser, | |
gitTool, | |
req.getParameter("git.authorOrCommitter") != null); | |
} | |
/** | |
* Determine the browser from the {@link StaplerRequest}. | |
* | |
* @param req | |
* @return | |
*/ | |
private GitRepositoryBrowser getBrowserFromRequest(StaplerRequest req) { | |
final GitRepositoryBrowser gitBrowser; | |
final String gitWebUrl = req.getParameter("gitweb.url"); | |
final String githubWebUrl = req.getParameter("githubweb.url"); | |
if (gitWebUrl != null && gitWebUrl.trim().length() > 0) { | |
try { | |
gitBrowser = new GitWeb(gitWebUrl.trim()); | |
} | |
catch (MalformedURLException e) { | |
throw new GitException("Error creating GitWeb", e); | |
} | |
} | |
else if (githubWebUrl != null && githubWebUrl.trim().length() > 0) { | |
try { | |
gitBrowser = new GithubWeb(githubWebUrl.trim()); | |
} | |
catch (MalformedURLException e) { | |
throw new GitException("Error creating GithubWeb", e); | |
} | |
} | |
else { | |
gitBrowser = null; | |
} | |
return gitBrowser; | |
} | |
public static List<RemoteConfig> createRepositoryConfigurations(String[] pUrls, | |
String[] repoNames, | |
String[] refSpecs, | |
File temp) { | |
List<RemoteConfig> remoteRepositories; | |
RepositoryConfig repoConfig = new RepositoryConfig(null, temp); | |
// Make up a repo config from the request parameters | |
String[] urls = pUrls; | |
String[] names = repoNames; | |
names = GitUtils.fixupNames(names, urls); | |
String[] refs = refSpecs; | |
if (names != null) { | |
for (int i = 0; i < names.length; i++) { | |
String name = names[i]; | |
name = name.replace(' ', '_'); | |
if(refs[i] == null || refs[i].length() == 0) { | |
refs[i] = "+refs/heads/*:refs/remotes/" + name + "/*"; | |
} | |
repoConfig.setString("remote", name, "url", urls[i]); | |
repoConfig.setString("remote", name, "fetch", refs[i]); | |
} | |
} | |
try { | |
repoConfig.save(); | |
remoteRepositories = RemoteConfig.getAllRemoteConfigs(repoConfig); | |
} | |
catch (Exception e) { | |
throw new GitException("Error creating repositories", e); | |
} | |
return remoteRepositories; | |
} | |
public static List<BranchSpec> createBranches(String[] branch) { | |
List<BranchSpec> branches = new ArrayList<BranchSpec>(); | |
String[] branchData = branch; | |
for(int i=0; i<branchData.length;i++) { | |
branches.add(new BranchSpec(branchData[i])); | |
} | |
if(branches.size() == 0) { | |
branches.add(new BranchSpec("*/master")); | |
} | |
return branches; | |
} | |
public static PreBuildMergeOptions createMergeOptions(String doMerge, String pMergeRemote, | |
String mergeTarget, | |
List<RemoteConfig> remoteRepositories) | |
throws FormException { | |
PreBuildMergeOptions mergeOptions = new PreBuildMergeOptions(); | |
if(doMerge != null && doMerge.trim().length() > 0) { | |
RemoteConfig mergeRemote = null; | |
String mergeRemoteName = pMergeRemote.trim(); | |
if (mergeRemoteName.length() == 0) | |
mergeRemote = remoteRepositories.get(0); | |
else | |
for (RemoteConfig remote : remoteRepositories) { | |
if (remote.getName().equals(mergeRemoteName)) { | |
mergeRemote = remote; | |
break; | |
} | |
} | |
if (mergeRemote==null) | |
throw new FormException("No remote repository configured with name '" + mergeRemoteName + "'", "git.mergeRemote"); | |
mergeOptions.setMergeRemote(mergeRemote); | |
mergeOptions.setMergeTarget(mergeTarget); | |
} | |
return mergeOptions; | |
} | |
public static GitWeb createGitWeb(String url) { | |
GitWeb gitWeb = null; | |
String gitWebUrl = url; | |
if (gitWebUrl != null && gitWebUrl.length() > 0) { | |
try { | |
gitWeb = new GitWeb(gitWebUrl); | |
} | |
catch (MalformedURLException e) { | |
throw new GitException("Error creating GitWeb", e); | |
} | |
} | |
return gitWeb; | |
} | |
public FormValidation doGitRemoteNameCheck(StaplerRequest req, StaplerResponse rsp) | |
throws IOException, ServletException { | |
String mergeRemoteName = req.getParameter("value"); | |
if (mergeRemoteName.length() == 0) | |
return FormValidation.ok(); | |
String[] urls = req.getParameterValues("git.repo.url"); | |
String[] names = req.getParameterValues("git.repo.name"); | |
names = GitUtils.fixupNames(names, urls); | |
for (String name : names) { | |
if (name.equals(mergeRemoteName)) | |
return FormValidation.ok(); | |
} | |
return FormValidation.error("No remote repository configured with name '" + mergeRemoteName + "'"); | |
} | |
} | |
private static final long serialVersionUID = 1L; | |
public boolean getDoGenerate() { | |
return this.doGenerateSubmoduleConfigurations; | |
} | |
public List<BranchSpec> getBranches() { | |
return branches; | |
} | |
public PreBuildMergeOptions getMergeOptions() { | |
return mergeOptions; | |
} | |
/** | |
* Look back as far as needed to find a valid BuildData. BuildData | |
* may not be recorded if an exception occurs in the plugin logic. | |
* @param build | |
* @param clone | |
* @return the last recorded build data | |
*/ | |
public BuildData getBuildData(Run build, boolean clone) { | |
BuildData buildData = null; | |
while (build != null) { | |
buildData = build.getAction(BuildData.class); | |
if (buildData != null) | |
break; | |
build = build.getPreviousBuild(); | |
} | |
if (buildData == null) | |
return clone? new BuildData() : null; | |
if (clone) | |
return buildData.clone(); | |
else | |
return buildData; | |
} | |
private static final Logger LOGGER = Logger.getLogger(GitSCM.class.getName()); | |
/** | |
* {@inheritDoc} | |
* | |
* Due to compatibility issues with older version we implement this ourselves instead of relying | |
* on the parent method. Kohsuke implemented a fix for this in the core (r21961), so we may drop | |
* this function after 1.325 is released. | |
* | |
* @todo: remove this function after 1.325 is released. | |
* | |
* @see <a href="https://hudson.dev.java.net/issues/show_bug.cgi?id=4514">#4514</a> | |
* @see <a href="http://fisheye4.atlassian.com/changelog/hudson/trunk/hudson?cs=21961">core fix</a> | |
*/ | |
//@Override | |
public List<Descriptor<RepositoryBrowser<?>>> getBrowserDescriptors() { | |
return RepositoryBrowsers.filter(GitRepositoryBrowser.class); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment