老旧Java Web应用实现增量自动化部署的一个方案

    如果你也有一个老旧的Java Web应用,因为各种原因,代码库中的代码是不完整的,所以每次上线只能增量部署,或者研发规范里就要求增量部署,在这种情况下如何实现自动化的编译和部署呢?下面给出一个可行的方案:

    1、部署前,将需要部署的代码合并到deliver分支

    2、比较deliver分支和master分支的差异,得到差异列表之后,用于后续的编译

    3、编译差异文件时,需要将应用所依赖的jar,已经部署的class文件等加入到CLASSPATH

    4、如果各测试环境也使用这一套自动部署的方案,可能还需要考虑剥离环境相关的配置到配置文件中,并根据环境分目录存放,具体可以参考maven的profile机制

    核心代码(代码结构请自行优化 :D):

    1、比较两个分支的差异(忽略whitespace带来的差异):

        try (Git git = Git.open(gitRepoFile);
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                DiffFormatter df = new DiffFormatter(out);) {

            Repository repository = git.getRepository();
            ObjectReader reader = repository.newObjectReader();

            String branchName = repository.getBranch();

            ObjectId masterId = repository.resolve("remotes/origin/master^{tree}");
            ObjectId branchId = repository.resolve(branchName + "^{tree}");

            CanonicalTreeParser masterTreeParser = new CanonicalTreeParser();
            masterTreeParser.reset(reader, masterId);

            CanonicalTreeParser branchTreeParser = new CanonicalTreeParser();
            branchTreeParser.reset(reader, branchId);

            List<DiffEntry> diffs = git.diff()
                    .setNewTree(branchTreeParser)
                    .setOldTree(masterTreeParser)
                    .call();

            Map<String, List<String>> diffFileMap = new HashMap<>();
            List<String> changeFileList = new ArrayList<>();
            List<String> deleteFileList = new ArrayList<>();

            df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
            df.setRepository(git.getRepository());

            for (DiffEntry diffEntry : diffs) {
                df.format(diffEntry);
                FileHeader fileHeader = df.toFileHeader(diffEntry);

                @SuppressWarnings("unchecked")
                List<HunkHeader> hunks = (List<HunkHeader>) fileHeader.getHunks();
                int changedSize = 0;
                for (HunkHeader hunkHeader : hunks) {
                    EditList editList = hunkHeader.toEditList();
                    for (Edit edit : editList) {
                        changedSize += edit.getLengthA() + edit.getLengthB();
                    }
                }

                if (changedSize > 0) {
                    ChangeType changeType = diffEntry.getChangeType();
                    if (ChangeType.DELETE.equals(changeType)) {
                        String oldFilePath = diffEntry.getOldPath();
                        log.info("{}|{}", changeType.name(), oldFilePath);
                        deleteFileList.add(oldFilePath);
                    } else {
                        String newFilePath = diffEntry.getNewPath();
                        log.info("{}|{}", changeType.name(), newFilePath);
                        changeFileList.add(newFilePath);
                    }
                }
            }
            diffFileMap.put("change", changeFileList);
            diffFileMap.put("delete", deleteFileList);

            return diffFileMap;
        }

    2、编译:

        Iterable<String> options = Arrays.asList(
                "-classpath", classpath,
                "-encoding", encoding,
                "-source", jdkVersion,
                "-target", jdkVersion,
                "-d", targetPath);

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        log.info("classpath:{}", classpath);
        log.info("targetPath:{}", targetPath);

        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);) {

            Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(fileList);

            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

            CompilationTask compilationTask = compiler
                    .getTask(null, fileManager, diagnostics, options, null, compilationUnits);

            if (!compilationTask.call()) {
                log.error("JavaCompiler Build failed:");

                for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
                    long line = diagnostic.getLineNumber();
                    String source = diagnostic.getSource() != null ? diagnostic.getSource().toString() : "";
                    String kind = diagnostic.getKind().name();
                    String message = diagnostic.getMessage(null);
                    log.info("{} on line:{} in {}.", kind, line, source);
                    log.info("message:{}", message);
                }
                return false;
            }
        }

    要实现完整的功能,还需要根据应用的具体情况完善,比如:从运行环境同步删除代码库中删除的文件;从代码库拉取文件时,用fetch,reset的方式;还可以借助Marathon、Mesos、Docker运行应用等。

猜你喜欢

转载自my.oschina.net/u/2288883/blog/1797722