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 文件夾裏面便是所有編譯後的變更文件,按照項目分級目錄存放,可以直接上傳目標服務器進行部署,如下:

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