package com.fitbank.fitpatch; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.prefs.Preferences; import org.apache.commons.beanutils.WrapDynaBean; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Transformer; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.BasicAuthenticationManager; import org.tmatesoft.svn.core.auth.SVNAuthentication; import org.tmatesoft.svn.core.auth.SVNSSHAuthentication; import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; import org.tmatesoft.svn.core.wc.ISVNStatusHandler; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNCommitPacket; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNRevisionRange; import org.tmatesoft.svn.core.wc.SVNStatus; import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.core.wc.SVNWCClient; import org.tmatesoft.svn.core.wc.SVNWCUtil; import com.fitbank.fitpatch.MantisQueries.ConnectionException; import com.fitbank.util.Debug; /** * Maneja un parche * * @author FitBank CI */ public final class PatchHandler { public static final List PROPERTIES = Arrays.asList("controlSources", "mantis", "username", "privateKey", "passphrase", "port"); private static Preferences preferences = Preferences.userNodeForPackage(PatchHandler.class); private MantisQueries mq; private SVNClientManager svn; private Map> revisionMap; public static String pad(Integer patchInt) { return StringUtils.leftPad(patchInt.toString(), 7, "0"); } public static void main(String[] args) throws NumberFormatException, SVNException, ConnectionException { PatchHandler patchHandler = new PatchHandler(); if (args.length < 1 || args[0].equals("help")) { System.err.println( "Uso: fitpatch [comando]"); System.err.println(""); System.err.println("Comandos:"); System.err.println("\thelp: muestra esta ayuda"); System.err.println("\tconfig list: muestra las propiedades"); System.err.println( "\tconfig set [propiedad] [valor]: cambia un valor de propiedad"); System.err.println( "\tmerge [incidencia]: hace merge de la incidencia"); System.err.println( "\tcommit: hace commit de la incidencia actual"); System.err.println( "\tcleanup: limpia la copia de trabajo"); System.exit(1); } String comando = args[0]; if ("config".equals(comando)) { patchHandler.config(args); System.exit(0); } patchHandler.connect(); if ("merge".equals(comando)) { patchHandler.merge(pad(Integer.parseInt(args[1]))); } else if ("commit".equals(comando)) { patchHandler.commit(); } else if ("cleanup".equals(comando)) { patchHandler.cleanup(); } } private void config(String[] args) { String action = args[1]; if ("list".equals(action)) { WrapDynaBean bean = new WrapDynaBean(this); for (String s : PROPERTIES) { System.out.println(s + "=" + bean.get(s)); } } else if ("set".equals(action)) { String key = args[2]; String value = args[3]; if (!PROPERTIES.contains(key)) { System.err.println("Propiedad no encontrada: " + key); System.exit(1); } if ("port".equals(action)) { preferences.putInt(key, Integer.parseInt(value)); } else { preferences.put(key, value); } } else { System.err.println("Comando de configuración incorrecto: " + action); System.exit(1); } } public String getControlSources() { return preferences.get("controlSources", ""); } public String getMantis() { return preferences.get("mantis", "192.168.1.13:3306"); } public String getUsername() { return preferences.get("username", ""); } public String getPrivateKey() { return preferences.get("privateKey", ""); } public String getPassphrase() { return preferences.get("passphrase", ""); } public int getPort() { return preferences.getInt("port", 22); } public Map> getRevisionMap() { return Collections.unmodifiableMap(revisionMap); } public void connect() throws ConnectionException { SVNSSHAuthentication auth = new SVNSSHAuthentication(getUsername(), new File(getPrivateKey()), getPassphrase(), getPort(), true); BasicAuthenticationManager authMgr = new BasicAuthenticationManager(new SVNAuthentication[] { auth }); svn = SVNClientManager.newInstance(SVNWCUtil.createDefaultOptions( false), authMgr); SVNRepositoryFactoryImpl.setup(); mq = new MantisQueries(getMantis(), "fitpatch", "fitpatch"); } public void query(String incidencia) { Debug.info("Usando incidencia #" + incidencia); revisionMap = mq.revisionMap(Long.parseLong(incidencia), "fitbank"); } public void merge(String incidencia) throws SVNException { if (loadIncidencia() != null) { throw new RuntimeException( "Actualmente se está trabajando en otra incidencia"); } query(incidencia); File controlDir = new File(getControlSources()); Debug.info("Revisando url de copia de trabajo..."); SVNInfo info = svn.getWCClient().doInfo(controlDir, SVNRevision.WORKING); if (!info.getURL().getPath().endsWith("/trunk")) { throw new Error("Carpeta seleccionada no corresponde con trunk"); } else { Debug.info("OK"); } Debug.info("Revisando cambios en copia de trabajo..."); Debug.info("OK"); Debug.info("Actualizando a la última revisión..."); Debug.info("OK"); for (String branch : revisionMap.keySet()) { Collection revisions = CollectionUtils.collect( revisionMap.get(branch), new Transformer() { public Object transform(Object input) { int n = (Integer) input; return new SVNRevisionRange(SVNRevision.create(n - 1), SVNRevision.create(n)); } }); SVNURL branchURL = svn.getWCClient().doInfo(controlDir, SVNRevision.WORKING).getRepositoryRootURL().appendPath( branch, true); Debug.info(String.format("Haciendo merge de %s...", branch)); svn.getDiffClient().doMerge(branchURL, SVNRevision.HEAD, revisions, controlDir, SVNDepth.INFINITY, true, false, false, false); Debug.info("OK"); } saveIncidencia(incidencia); Debug.info("Revisando conflictos..."); Debug.info("OK"); } public void commit() throws SVNException { String incidencia = loadIncidencia(); query(incidencia); File controlDir = new File(getControlSources()); Debug.info("Haciendo commit..."); SVNCommitPacket cp = svn.getCommitClient().doCollectCommitItems( new File[] { controlDir }, false, false, SVNDepth.INFINITY, null); SVNCommitInfo info = svn.getCommitClient().doCommit(cp, false, String. format("Paso a control de la incidencia #%s", incidencia)); if (info.getErrorMessage() != null) { throw new Error("Error al hacer commit: " + info.getErrorMessage()); } else { Debug.info("OK"); } deleteIncidencia(); } public void cleanup() throws SVNException { Debug.info("Limpiando copia de trabajo..."); File controlDir = new File(getControlSources()); SVNWCClient wc = svn.getWCClient(); wc.setRevertMissingDirectories(true); wc.doCleanup(controlDir, true); wc.doRevert(new File[] { controlDir }, SVNDepth.INFINITY, Collections.EMPTY_LIST); svn.getStatusClient().doStatus(controlDir, SVNRevision.WORKING, SVNDepth.INFINITY, false, true, false, false, new CleanStatusHandler(), Collections.EMPTY_LIST); } private void saveIncidencia(String incidencia) { File file = new File(getControlSources(), ".fitpatch"); try { FileWriter writer = new FileWriter(file); IOUtils.write(incidencia, writer); writer.close(); } catch (IOException ex) { throw new RuntimeException(ex); } } private String loadIncidencia() { File file = new File(getControlSources(), ".fitpatch"); if (!file.exists()) { return null; } try { return IOUtils.toString(new FileReader(file)).trim(); } catch (IOException ex) { return null; } } private void deleteIncidencia() { File file = new File(getControlSources(), ".fitpatch"); if (file.exists()) { file.delete(); } } private static class CleanStatusHandler implements ISVNStatusHandler { public void handleStatus(SVNStatus status) throws SVNException { if (status.getContentsStatus() == SVNStatusType.STATUS_UNVERSIONED) { status.getFile().delete(); } } } }