Svn提交记录、SvnKit组件、增量部署代码工具

前言

实际开发中,因为开发人员往往在版本库中独享一个分支,或者几个负责同一模块的组员共享一个分支,一个模块开发完成后,代码以增量的方式部署测试服务器或者生产服务器,而非全量部署,因此,开发人员在完成代码编写后,需要整理自己更改过的文件都有哪些,然后在自己开发机器上找到文件对应的编译后的 .class 文件,最后才是部署到目标服务器,这一过程手动整理的话往往容易出错,落下一个文件都是致命的,于是乎我自己写了一个工具类,所有工作一键解决,再也不担心出错了,现在特分享出来,希望对大家有帮助。

功能

 * 1.查询指定 svn 账户指定时间段的所有提交记录; 
 * 2.根据提交记录整理出指定用户开发机上所有变更文件的绝对路径;
 * 3.清洗重复的数据、文件夹、删除文件、复制改名文件等数据,得到有效路径; 
 * 4.将有效路径数据写入文档,提供开发人员核对,包括两个文档,一个是可以直接正常部署的路径文档,一个是删除或者修改                 了某文件名提醒文档,提醒是否对应删除或修改目标服务器对应文件;
 * 5.调用cmd窗口执行windows命令,生成开发机上所有类型的变更文件压缩jar文件,所有文件归入各自绝对项目路径;
 * 6.调用cmd窗口执行windows命令,解压jar文件,获得可参照包名直接部署到目标服务器的项目文件;
 PS:可以配多个svn账号,解决多员工协同操作同一个文件问题,只要保证所有参与人和svn同步,无需考虑操作先后

 代码

导包lib:

工具类代码如下:

package com.svnkit;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

/**
 * SvnLogConversionUtil svn组件工具 
 * 1.查询指定 svn 账户指定时间段的所有提交记录; 
 * 2.根据提交记录整理出指定用户开发机上所有变更文件的绝对路径;
 * 3.清洗重复的数据、文件夹、删除文件、复制改名文件等数据,得到有效路径; 
 * 4.将有效路径数据写入文档,提供开发人员核对;
 * 5.生成开发机上所有类型的变更文件压缩jar文件,所有文件归入各自绝对项目路径;
 * 6.解压jar文件,获得可参照包名直接部署到目标服务器的项目文件;
 * 
 * PS:可以配多个svn账号,解决多员工协同操作同一个文件问题,只要保证所有参与人和svn同步,无需考虑操作先后
 * 
 * @author weny.yang create date: 2020-05-20 09:00:00
 */
public class SvnLogConversionUtil {
	private static Log log = LogFactory.getLog(SvnLogConversionUtil.class);

	private String userName;
	private String password;
	private String svnUrl;
	
	private String staDate;
	private String endDate;
	
	// File export path
	private String writeUrl;
		
	// native project path
	private String nativeProjectUrl;
	// content directory name: WebContent or WebRoot
	private String contentDirectory;
	
	private List<String> otherUserList = new ArrayList<String>();
	
	private Collection<SVNLogEntry> logEntries;
	private Map<String, Set<String>> cateMap = new HashMap<String, Set<String>>();
	private List<String> cateList = new ArrayList<String>();
	private Map<String, Set<String>> delOrCopyMap = new HashMap<String, Set<String>>();
	private List<String> delOrCopyList = new ArrayList<String>();
    private String tempDir = System.getProperty("java.io.tmpdir");
    private DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(true);
    private SVNRepository repos;
    private ISVNAuthenticationManager authManager;

    /**
     * 构造方法
     */
	public SvnLogConversionUtil(String userName, String password, String svnUrl, String staDate, String endDate,
			String writeUrl, String nativeProjectUrl, String contentDirectory, List<String> otherUserList) {
		try {
			this.userName = userName;
			this.password = password;
			this.svnUrl = svnUrl;
			this.staDate = staDate;
        	this.endDate = endDate;
        	this.writeUrl = writeUrl;
        	this.nativeProjectUrl = nativeProjectUrl;
        	this.contentDirectory = contentDirectory;
        	this.otherUserList = otherUserList;
            init();
        } catch (SVNException e) {
            e.printStackTrace();
        }
    }
	/**
     * 初始化
     */
    private void init() throws SVNException{
        log.info("init start...");
        authManager = SVNWCUtil.createDefaultAuthenticationManager(new File(tempDir+"/auth"), userName, password.toCharArray());
        options.setDiffCommand("-x -w");
        repos = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(svnUrl));
        repos.setAuthenticationManager(authManager);
        log.info("init completed");
    }
	
    
    /**
     * 获取指定版本号区间所有提交记录
     */
    public void pathConversion() throws SVNException, IOException, ParseException {
    	//先清空相关文件夹
    	delAllFile(new File(writeUrl+userName));
    	//初始化工具类
    	init();
    	//获取提交记录
    	queryLogByRevision();
    	//清洗数据
    	washData();
    	//写数据
    	writeWellFile();
    	//执行cmd命令
    	exeCommand();
    }

	/**
	 * 获取指定版本号区间所有提交记录
	 */
	@SuppressWarnings("unchecked")
	private void queryLogByRevision() throws SVNException, IOException, ParseException {
		long startRevision = repos.getDatedRevision(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(staDate));
		long endRevision = repos.getDatedRevision(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(endDate));
		logEntries = repos.log(new String[] {""}, null, startRevision, endRevision, true, true);
	}
	
	/**
	 * 数据清洗
	 */
	private String strReplace(String path) {
		path = path.replace("/", java.io.File.separator);
		if (path.contains("src")) {
			path = contentDirectory + java.io.File.separator + "WEB-INF" + java.io.File.separator
					+ "classes" + path.substring(path.indexOf("src") + 3, path.length());
		}
		if (path.contains(contentDirectory)) {
			path = nativeProjectUrl + java.io.File.separator
					+ path.substring(path.indexOf(contentDirectory), path.length());
		}
		if (path.contains(".java")) {
			path = path.replace(".java", ".class");
		}
		return path;
	}
	/**
	 * 数据清洗
	 */
	private void washData() throws IOException {
		for (Iterator<SVNLogEntry> entries = logEntries.iterator(); entries.hasNext();) {
			SVNLogEntry logEntry = (SVNLogEntry) entries.next();
			if (logEntry.getAuthor().equals(userName) || otherUserList.contains(logEntry.getAuthor())) {
				if (logEntry.getChangedPaths().size() > 0) {
					Set<String> changedPathsSet = logEntry.getChangedPaths().keySet();
					for (Iterator<String> changedPaths = changedPathsSet.iterator(); changedPaths.hasNext();) {
						SVNLogEntryPath entryPath = (SVNLogEntryPath) logEntry.getChangedPaths().get(changedPaths.next());
						String path = entryPath.getPath();
						path = this.strReplace(path);
						//新增、修改类变更文件
						if (new File(path).exists() && path.contains(".")) {
							String key = path.substring(path.lastIndexOf(".") + 1);
							if (cateMap.containsKey(key)) {
								Set<String> cateSet = cateMap.get(key);
								cateSet.add(path);
							} else {
								Set<String> cateSet = new HashSet<String>();
								cateSet.add(path);
								cateMap.put(key, cateSet);
								cateList.add(key);
							}
						}
						//删除类变更文件
						if (!new File(path).exists() && path.contains(".")) {
							String key = path.substring(path.lastIndexOf(".") + 1);
							if (delOrCopyMap.containsKey(key)) {
								Set<String> cateSet = delOrCopyMap.get(key);
								cateSet.add(path);
							} else {
								Set<String> cateSet = new HashSet<String>();
								cateSet.add(path);
								delOrCopyMap.put(key, cateSet);
								delOrCopyList.add(key);
							}
						}
						//复制类变更文件
						String copyPath = entryPath.getCopyPath();
						if(copyPath !=null) {
							copyPath = this.strReplace(copyPath);
							String key = copyPath.substring(copyPath.lastIndexOf(".") + 1);
							if (delOrCopyMap.containsKey(key)) {
								Set<String> cateSet = delOrCopyMap.get(key);
								cateSet.add(copyPath);
							} else {
								Set<String> cateSet = new HashSet<String>();
								cateSet.add(copyPath);
								delOrCopyMap.put(key, cateSet);
								delOrCopyList.add(key);
							}
						}
					}
				}
			}
		}
	}

	/**
	 * 写入文档
	 */
	private void writeWellFile() throws IOException {
		File file = new File(writeUrl + java.io.File.separator + userName);
		File writeName = new File(writeUrl + java.io.File.separator + userName + java.io.File.separator + userName + ".txt");
		if (!file.exists()) {
			file.mkdirs();
		}
		writeName.createNewFile();
		FileWriter writer = new FileWriter(writeName);
		@SuppressWarnings("resource")
		BufferedWriter out = new BufferedWriter(writer);
		for (String cate : cateList) {
			Iterator<Entry<String, Set<String>>> iteratorMap = cateMap.entrySet().iterator();
			while (iteratorMap.hasNext()) {
				Entry<String, Set<String>> entry = iteratorMap.next();
				if (entry.getKey().equals(cate)) {
					Set<String> cateSet = entry.getValue();
					Iterator<String> iteratorSet = cateSet.iterator();
					while (iteratorSet.hasNext()) {
						String str = iteratorSet.next();
						//System.lineSeparator()本质是"\r\n", jdk1.8方法,如果报错,自行替换即可
						str += System.lineSeparator();
						out.write(str);
					}
				}
			}
			out.write("\n");
		}
		out.flush();
		if(delOrCopyList.size()>0) {
			writeWarnFile();
		}
		log.info("write catalog file completed, please check here >>> " + writeUrl + userName + java.io.File.separator + userName);
	}

	/**
	 * 写入警告文档
	 */
	private void writeWarnFile() throws IOException {
		File file = new File(writeUrl + java.io.File.separator + userName);
		File writeName = new File(writeUrl + java.io.File.separator + userName + java.io.File.separator + "警告.txt");
		if (!file.exists()) {
			file.mkdirs();
		}
		writeName.createNewFile();
		FileWriter writer = new FileWriter(writeName);
		@SuppressWarnings("resource")
		BufferedWriter out = new BufferedWriter(writer);
		out.write("注意 >>> 以下内容为复制改名项或本地已删除项,请确认目标环境是否需要变更!\r\n");
		out.write("\r\n");
		for (String cate : delOrCopyList) {
			Iterator<Entry<String, Set<String>>> iteratorMap = delOrCopyMap.entrySet().iterator();
			while (iteratorMap.hasNext()) {
				Entry<String, Set<String>> entry = iteratorMap.next();
				if (entry.getKey().equals(cate)) {
					Set<String> cateSet = entry.getValue();
					Iterator<String> iteratorSet = cateSet.iterator();
					while (iteratorSet.hasNext()) {
						String str = iteratorSet.next();
						str += System.lineSeparator();
						out.write(str);
					}
				}
			}
			out.write("\n");
		}
		out.flush();
	}
	
	/**
	 * 执行cmd命令
	 */
	private void exeCommand() throws IOException {
		Runtime.getRuntime().exec("cmd /c " + writeUrl.substring(0, 2) + " && cd " + writeUrl + userName + " && start jar -cvf " + userName + ".jar @" + userName + ".txt");
		boolean flg1 = true;
		while (flg1) {
			if (new File(writeUrl + userName + java.io.File.separator + userName + ".jar").exists()) {
				flg1 = false;
				log.info("create jar file completed, please check here >>> " + writeUrl + userName + java.io.File.separator + userName + ".jar");
				Runtime.getRuntime().exec("cmd /c " + writeUrl.substring(0, 2) + " && cd " + writeUrl + userName + " && jar xvf " + userName + ".jar");
				boolean flg2 = true;
				while (flg2) {
					if (new File(writeUrl + userName + java.io.File.separator + "META-INF").exists()) {
						flg2 = false;
						log.info("decompression file completed, please check here >>> " + writeUrl + userName);
					}
				}
			}
		}
	}
	
	
	/**
	 * 删除文件或文件夹
	 */
	private void delAllFile(File directory){
	    if (!directory.isDirectory()){
	        directory.delete();
	    } else{
	        File [] files = directory.listFiles();
	        // 空文件夹
	        if (files.length == 0){
	            directory.delete();
	            //System.out.println("删除" + directory.getAbsolutePath());
	            return;
	        }
	        // 删除子文件夹和子文件
	        for (File file : files){
	            if (file.isDirectory()){
	                delAllFile(file);
	            } else {
	                file.delete();
	               //System.out.println("删除" + file.getAbsolutePath());
	            }
	        }
	        // 删除文件夹本身
	        directory.delete();
	        //System.out.println("删除" + directory.getAbsolutePath());
	    }
	}
}

测试类代码如下:

package com.svnkit;

import java.util.ArrayList;
import java.util.List;

public class TestSvnKit{
	public static void main(String[] arg) {
		try {
			// svn账户、密码、地址
			String userName = "ITL520";
			String password = "xxx";
			String svnUrl = "svn://xxxxxx/Image&PCM/ImageCode/imaging";

			// 查询提交记录的时间区间
			String staDate = "2020-05-20 00:00:00";
			String endDate = "2020-05-21 23:59:59";

			// 文件输出路径
			String writeUrl = "C:\\Users\\86158\\Desktop";

			// 本地开发机上项目路径,截止到项目名
			String nativeProjectUrl = "D:\\Software\\Java\\WorkSpace\\imaging";
			// 项目的content directory名: WebContent 或者 WebRoot
			String contentDirectory = "WebRoot";

			// 如果多人协同操作同一模块,将参与用户的svn账户配到此处
			List<String> otherUserList = new ArrayList<String>();
			// otherUserList.add("ITL521");

			SvnLogConversionUtil SvnUtil = new SvnLogConversionUtil(userName, password, svnUrl, staDate, endDate, writeUrl, nativeProjectUrl, contentDirectory, otherUserList);
			SvnUtil.pathConversion();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

运行演示

第一步:运行 TestSvnKit 中的 main 方法,桌面生成文件夹如下图:

第二步:打开文件夹,内部结构如下:

txt文档里面写入的是变更了那些类文件的本地绝对路径信息,会根据文件类型自动分类,打开如下:

如果有删除或者给某个文件改名了,会额外生成一个 警告.txt 文档,记录变更信息,以便开发人员核对。

Software 文件夹里面便是所有编译后的变更文件,按照项目分级目录存放,可以直接上传目标服务器进行部署,如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章