From 794afd76127191e6a5174647d0c2620f0aadd915 Mon Sep 17 00:00:00 2001 From: Michael Clarke Date: Sun, 5 Feb 2012 11:36:43 +0000 Subject: [PATCH] [FIXED JENKINS-753] Allows performing a clean checkout if update fails [FIXED JENKINS-12595] CVS gets passed the module name so creates lock files in the correct directory on the remote server [FIXED JENKINS-12581] CVS plugin now forces a module name in the udpate command to prevent an attempt to checkout all remote modules --- src/main/java/hudson/scm/CVSSCM.java | 159 ++++++++++++------ .../resources/hudson/scm/CVSSCM/config.jelly | 3 + 2 files changed, 106 insertions(+), 56 deletions(-) diff --git a/src/main/java/hudson/scm/CVSSCM.java b/src/main/java/hudson/scm/CVSSCM.java index fab4359..ae77df2 100644 --- a/src/main/java/hudson/scm/CVSSCM.java +++ b/src/main/java/hudson/scm/CVSSCM.java @@ -126,6 +126,8 @@ public class CVSSCM extends SCM implements Serializable { private boolean pruneEmptyDirectories; private boolean disableCvsQuiet; + + private boolean cleanOnFailedUpdate; // start legacy fields @Deprecated @@ -153,12 +155,13 @@ public CVSSCM(final String cvsRoot, final String allModules, final String branch final boolean canUseUpdate, final boolean useHeadIfNotFound, final boolean legacy, final boolean isTag, final String excludedRegions) { this(LegacyConvertor.getInstance().convertLegacyConfigToRepositoryStructure(cvsRoot, allModules, branch, isTag, excludedRegions, - useHeadIfNotFound), canUseUpdate, legacy, null, Boolean.getBoolean(CVSSCM.class.getName() + ".skipChangeLog"), true, false); + useHeadIfNotFound), canUseUpdate, legacy, null, Boolean.getBoolean(CVSSCM.class.getName() + ".skipChangeLog"), true, false, false); } @DataBoundConstructor public CVSSCM(final List repositories, final boolean canUseUpdate, final boolean legacy, - final CVSRepositoryBrowser browser, final boolean skipChangeLog, final boolean pruneEmptyDirectories, final boolean disableCvsQuiet) { + final CVSRepositoryBrowser browser, final boolean skipChangeLog, final boolean pruneEmptyDirectories, + final boolean disableCvsQuiet, final boolean cleanOnFailedUpdate) { this.repositories = repositories.toArray(new CvsRepository[repositories.size()]); this.canUseUpdate = canUseUpdate; this.skipChangeLog = skipChangeLog; @@ -166,6 +169,7 @@ public CVSSCM(final List repositories, final boolean canUseUpdate repositoryBrowser = browser; this.pruneEmptyDirectories = pruneEmptyDirectories; this.disableCvsQuiet = disableCvsQuiet; + this.cleanOnFailedUpdate = cleanOnFailedUpdate; } @@ -645,6 +649,11 @@ public boolean isFlatten() { public boolean isDisableCvsQuiet() { return disableCvsQuiet; } + + @Exported + public boolean isCleanOnFailedUpdate() { + return cleanOnFailedUpdate; + } public boolean isLegacy() { return !flatten; @@ -705,11 +714,7 @@ public boolean checkout(final AbstractBuild build, final Launcher launcher final FilePath module = workspace.child(cvsModule.getCheckoutName()); - final Client cvsClient = getCvsClient(repository, envVars); - final GlobalOptions globalOptions = getGlobalOptions(repository, envVars); - - final Command cvsCommand; - + boolean updateFailed = false; boolean update = false; if (flatten) { @@ -721,7 +726,13 @@ public boolean checkout(final AbstractBuild build, final Launcher launcher update = true; } } + + final FilePath targetWorkspace = flatten ? workspace.getParent() : workspace; + final String moduleName= flatten ?workspace.getName() : cvsModule.getCheckoutName(); + + + // we're doing an update if (update) { // we're doing a CVS update UpdateCommand updateCommand = new UpdateCommand(); @@ -748,9 +759,22 @@ public boolean checkout(final AbstractBuild build, final Launcher launcher updateCommand.setUpdateByRevision(CvsModuleLocationType.HEAD.getName().toUpperCase()); updateCommand.setUpdateByDate(dateStamp); } + + if (!perform(updateCommand, targetWorkspace, listener, repository, moduleName, envVars)) { + updateFailed = true; + } - cvsCommand = updateCommand; - } else { + } + + + // we're doing a checkout + if (!update || (updateFailed && cleanOnFailedUpdate)) { + + if (updateFailed) { + listener.getLogger().println("Update failed. Cleaning workspace and performing full checkout"); + workspace.deleteContents(); + } + // we're doing a CVS checkout CheckoutCommand checkoutCommand = new CheckoutCommand(); @@ -774,58 +798,16 @@ public boolean checkout(final AbstractBuild build, final Launcher launcher // set directory pruning checkoutCommand.setPruneDirectories(isPruneEmptyDirectories()); - // tell it what to checkout the module as - if we're - // flattening then we ignore any user set name - if (flatten) { - checkoutCommand.setCheckoutDirectory(workspace.getName()); - } else { - checkoutCommand.setCheckoutDirectory(cvsModule.getCheckoutName()); - } + // set where we're checking out to + checkoutCommand.setCheckoutDirectory(moduleName); // and specify which module to load checkoutCommand.setModule(cvsModule.getRemoteName()); - cvsCommand = checkoutCommand; - - } - - listener.getLogger().println("cvs " + cvsCommand.getCVSCommand()); - - final FilePath targetWorkspace = flatten ? workspace.getParent() : update ? workspace.child(cvsModule - .getCheckoutName()) : workspace; - - if (!targetWorkspace.act(new FileCallable() { - - private static final long serialVersionUID = -7517978923721181408L; - - @Override - public Boolean invoke(final File workspace, final VirtualChannel channel) throws RuntimeException { - cvsClient.setLocalPath(workspace.getAbsolutePath()); - final BasicListener basicListener = new BasicListener(listener.getLogger(), listener.getLogger()); - cvsClient.getEventManager().addCVSListener(basicListener); - - try { - return cvsClient.executeCommand(cvsCommand, globalOptions); - } catch (CommandAbortedException e) { - e.printStackTrace(listener.error("CVS Command aborted: " + e.getMessage())); - return false; - } catch (CommandException e) { - e.printStackTrace(listener.error("CVS Command failed: " + e.getMessage())); - return false; - } catch (AuthenticationException e) { - e.printStackTrace(listener.error("CVS Authentication failed: " + e.getMessage())); - return false; - } finally { - try { - cvsClient.getConnection().close(); - } catch(IOException ex) { - listener.error("Could not close client connection: " + ex.getMessage()); - } - } + if (!perform(checkoutCommand, workspace, listener, repository, moduleName, envVars)) { + return false; } - })) { - listener.error("Cvs task failed"); - return false; + } } @@ -870,6 +852,71 @@ public Void invoke(final File f, final VirtualChannel channel) throws IOExceptio return true; } + + /** + * Runs a cvs command in the given workspace. + * @param cvsCommand the command to run (checkout, update etc) + * @param workspace the workspace to run the command in + * @param listener where to log output to + * @param repository the repository to connect to + * @param moduleName the name of the directory within the workspace that will have work performed on it + * @param envVars the environmental variables to expand + * @return true if the action succeeds, false otherwise + * @throws IOException on failure handling files or server actions + * @throws InterruptedException if the user cancels the action + */ + private boolean perform(final Command cvsCommand, final FilePath workspace, final TaskListener listener, + final CvsRepository repository, final String moduleName, final EnvVars envVars) throws IOException, InterruptedException { + + final Client cvsClient = getCvsClient(repository, envVars); + final GlobalOptions globalOptions = getGlobalOptions(repository, envVars); + + + if (!workspace.act(new FileCallable() { + + private static final long serialVersionUID = -7517978923721181408L; + + @Override + public Boolean invoke(final File workspace, final VirtualChannel channel) throws RuntimeException { + + + if (cvsCommand instanceof UpdateCommand) { + ((UpdateCommand) cvsCommand).setFiles(new File[]{new File(workspace, moduleName)}); + } + + listener.getLogger().println("cvs " + cvsCommand.getCVSCommand()); + + + cvsClient.setLocalPath(workspace.getAbsolutePath()); + final BasicListener basicListener = new BasicListener(listener.getLogger(), listener.getLogger()); + cvsClient.getEventManager().addCVSListener(basicListener); + + try { + return cvsClient.executeCommand(cvsCommand, globalOptions); + } catch (CommandAbortedException e) { + e.printStackTrace(listener.error("CVS Command aborted: " + e.getMessage())); + return false; + } catch (CommandException e) { + e.printStackTrace(listener.error("CVS Command failed: " + e.getMessage())); + return false; + } catch (AuthenticationException e) { + e.printStackTrace(listener.error("CVS Authentication failed: " + e.getMessage())); + return false; + } finally { + try { + cvsClient.getConnection().close(); + } catch(IOException ex) { + listener.error("Could not close client connection: " + ex.getMessage()); + } + } + } + })) { + listener.error("Cvs task failed"); + return false; + } + + return true; + } private Map> calculateWorkspaceState(final FilePath workspace) throws IOException, InterruptedException { diff --git a/src/main/resources/hudson/scm/CVSSCM/config.jelly b/src/main/resources/hudson/scm/CVSSCM/config.jelly index 6bda231..87efef4 100644 --- a/src/main/resources/hudson/scm/CVSSCM/config.jelly +++ b/src/main/resources/hudson/scm/CVSSCM/config.jelly @@ -42,5 +42,8 @@ THE SOFTWARE. + + + \ No newline at end of file