package org.apache.maven.shared.release; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import hudson.plugins.stagingrelease.StagingReleaseManager; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.maven.project.MavenProject; import org.apache.maven.settings.Settings; import org.apache.maven.shared.release.config.ReleaseDescriptor; import org.apache.maven.shared.release.config.ReleaseDescriptorStore; import org.apache.maven.shared.release.config.ReleaseDescriptorStoreException; import org.apache.maven.shared.release.phase.ReleasePhase; import org.codehaus.plexus.logging.AbstractLogEnabled; import org.codehaus.plexus.util.StringUtils; /** * Implementation of the release manager. * */ public class DefaultReleaseManager extends AbstractLogEnabled implements StagingReleaseManager { /** * The phases of release to run, and in what order. */ private List<String> stagingReleasePhases; private List<String> setVersionPhases; /** * The available phases. */ private Map<String, ReleasePhase> releasePhases; /** * The configuration storage. */ private ReleaseDescriptorStore configStore; private static final int PHASE_SKIP = 0, PHASE_START = 1, PHASE_END = 2, GOAL_START = 11, GOAL_END = 12, ERROR = 99; public void stagingRelease(ReleaseDescriptor releaseDescriptor, Settings settings, List<MavenProject> reactorProjects, boolean resume, boolean dryRun) throws ReleaseExecutionException, ReleaseFailureException { stagingRelease(releaseDescriptor, settings, reactorProjects, resume, dryRun, null, null); } private void stagingRelease(ReleaseDescriptor releaseDescriptor, Settings settings, List<MavenProject> reactorProjects, boolean resume, boolean dryRun, ReleaseManagerListener listener, ReleaseResult result) throws ReleaseExecutionException, ReleaseFailureException { updateListener(listener, "stagingRelease", GOAL_START); ReleaseDescriptor config; if (resume) { config = loadReleaseDescriptor(releaseDescriptor, listener); } else { config = releaseDescriptor; } // Later, it would be a good idea to introduce a proper workflow tool so // that the release can be made up of a // more flexible set of steps. String completedPhase = config.getCompletedPhase(); int index = stagingReleasePhases.indexOf(completedPhase); for (int idx = 0; idx <= index; idx++) { updateListener(listener, stagingReleasePhases.get(idx).toString(), PHASE_SKIP); } if (index == stagingReleasePhases.size() - 1) { logInfo( result, "Release preparation already completed. You can now continue with release:perform, " + "or start again using the -Dresume=false flag"); } else if (index >= 0) { logInfo(result, "Resuming release from phase '" + stagingReleasePhases.get(index + 1) + "'"); } // start from next phase for (int i = index + 1; i < stagingReleasePhases.size(); i++) { String name = (String) stagingReleasePhases.get(i); ReleasePhase phase = (ReleasePhase) releasePhases.get(name); if (phase == null) { throw new ReleaseExecutionException("Unable to find phase '" + name + "' to execute"); } updateListener(listener, name, PHASE_START); ReleaseResult phaseResult = null; try { if (dryRun) { phaseResult = phase.simulate(config, settings, reactorProjects); } else { phaseResult = phase.execute(config, settings, reactorProjects); } } finally { if (result != null && phaseResult != null) { result.getOutputBuffer().append(phaseResult.getOutput()); } } config.setCompletedPhase(name); try { configStore.write(config); } catch (ReleaseDescriptorStoreException e) { // TODO: rollback? throw new ReleaseExecutionException( "Error writing release properties after completing phase", e); } updateListener(listener, name, PHASE_END); } // call release:clean so that resume will not be possible anymore after // a perform clean(releaseDescriptor, listener, reactorProjects); updateListener(listener, "stagingRelease", GOAL_END); } /** * Determines the path of the working directory. By default, this is the * checkout directory. For some SCMs, the project root directory is not the * checkout directory itself, but a SCM-specific subdirectory. * * @param checkoutDirectory * The checkout directory as java.io.File * @param relativePathProjectDirectory * The relative path of the project directory within the checkout * directory or "" * @return The working directory */ protected File determineWorkingDirectory(File checkoutDirectory, String relativePathProjectDirectory) { if (StringUtils.isNotEmpty(relativePathProjectDirectory)) { return new File(checkoutDirectory, relativePathProjectDirectory); } else { return checkoutDirectory; } } private ReleaseDescriptor loadReleaseDescriptor( ReleaseDescriptor releaseDescriptor, ReleaseManagerListener listener) throws ReleaseExecutionException { try { updateListener(listener, "verify-release-configuration", PHASE_START); ReleaseDescriptor descriptor = configStore.read(releaseDescriptor); updateListener(listener, "verify-release-configuration", PHASE_END); return descriptor; } catch (ReleaseDescriptorStoreException e) { updateListener(listener, e.getMessage(), ERROR); throw new ReleaseExecutionException( "Error reading stored configuration: " + e.getMessage(), e); } } public void clean(ReleaseDescriptor releaseDescriptor, ReleaseManagerListener listener, List<MavenProject> reactorProjects) { updateListener(listener, "cleanup", PHASE_START); getLogger().info("Cleaning up after release..."); configStore.delete(releaseDescriptor); for (Iterator<String> i = stagingReleasePhases.iterator(); i.hasNext();) { String name = i.next(); ReleasePhase phase = releasePhases.get(name); phase.clean(reactorProjects); } updateListener(listener, "cleanup", PHASE_END); } void setConfigStore(ReleaseDescriptorStore configStore) { this.configStore = configStore; } void updateListener(ReleaseManagerListener listener, String name, int state) { if (listener != null) { switch (state) { case GOAL_START: listener.goalStart(name, getGoalPhases(name)); break; case GOAL_END: listener.goalEnd(); break; case PHASE_SKIP: listener.phaseSkip(name); break; case PHASE_START: listener.phaseStart(name); break; case PHASE_END: listener.phaseEnd(); break; default: listener.error(name); } } } private List<String> getGoalPhases(String name) { List<String> phases = new ArrayList<String>(); if ("stagingRelease".equals(name)) { phases.addAll(this.stagingReleasePhases); } return Collections.unmodifiableList(phases); } private void logInfo(ReleaseResult result, String message) { if (result != null) { result.appendInfo(message); } getLogger().info(message); } private void captureException(ReleaseResult result, ReleaseManagerListener listener, Exception e) { updateListener(listener, e.getMessage(), ERROR); result.appendError(e); result.setResultCode(ReleaseResult.ERROR); } public void setVersion(ReleaseDescriptor releaseDescriptor, Settings settings, List<MavenProject> reactorProjects, ReleaseManagerListener listener) { ReleaseResult result = new ReleaseResult(); result.setStartTime(System.currentTimeMillis()); try { setVersion(releaseDescriptor, settings, reactorProjects, listener, result); result.setResultCode(ReleaseResult.SUCCESS); } catch (ReleaseExecutionException e) { captureException(result, listener, e); } catch (ReleaseFailureException e) { captureException(result, listener, e); } finally { result.setEndTime(System.currentTimeMillis()); } } private void setVersion(ReleaseDescriptor releaseDescriptor, Settings settings, List<MavenProject> reactorProjects, ReleaseManagerListener listener, ReleaseResult result) throws ReleaseExecutionException, ReleaseFailureException { updateListener(listener, "stagingRelease", GOAL_START); ReleaseDescriptor config = releaseDescriptor; // start from next phase for (int i = 0; i < setVersionPhases.size(); i++) { String name = (String) setVersionPhases.get(i); ReleasePhase phase = (ReleasePhase) releasePhases.get(name); if (phase == null) { throw new ReleaseExecutionException("Unable to find phase '" + name + "' to execute"); } updateListener(listener, name, PHASE_START); ReleaseResult phaseResult = null; try { phaseResult = phase.execute(config, settings, reactorProjects); } finally { if (result != null && phaseResult != null) { result.getOutputBuffer().append(phaseResult.getOutput()); } } config.setCompletedPhase(name); updateListener(listener, name, PHASE_END); } // call release:clean so that resume will not be possible anymore after // a perform clean(releaseDescriptor, listener, reactorProjects); updateListener(listener, "stagingRelease", GOAL_END); } }