CCS项目持续集成

​ 因工作需要,用户提出希望可以做ccs项目的持续集成,及代码提交后能够自动编译并提交到svn。调研过jenkins之后发现重新手写更有性价比,所以肝了几晚终于搞出来了,现在分享出来。

​ 先交代背景:

	1.	代码分两部分,一部分在git上,一部分在svn上
	2.	希望git上提交的代码时和svn上提交代码时都触发持续集成。

​ 实现功能:

  1. git上提交代码时自动触发持续集成

  2. svn上提交代码时,并在备注中以“编译”开头时触发持续集成

  3. 持续集成功能:

    a. 将git上的代码复制到 svn的 编译目录(记为 X)中

    b. 将svn的源码目录(记为S)复制到svn的编译目录X的子目录(X1)中

    c. 执行ccs的编译命令,编译ccs项目,

    d. 将编译出的结果文件分别复制到 svn的多个目录中,

    e. 将编译结果文件提交到svn,备注日志中包括git上的版本信息、svn源码目录(S)的版本信息。

实现说明:

  1. 使用springboot 搭建一个web项目,并提供一个接口用户触发持续集成,记为接口X

  2. 在git配置webhook,在代码检入时调用接口X (下面的配置需要使用管理员的账号)

    在这里插入图片描述

  3. 在svn中编写钩子函数,在备注信息以”编译“开头时,调用接口x

    # 构造函数代码片段,此代码在svn的仓库目录下的hooks目中,文件名称为 post-commit  对的,没有后缀
    COMMENT=$(svnlook log -r $REV $REPOS)
    
    if echo "$COMMENT" | grep -qE '^编译'; then
      echo "提交日志以'编译'开头。"  >> ${SVN_LOG_FILE_PATH}
      curl -X post -v http://xxxx/cicd/xxx #这个就是接口x的地址了
    
  4. 接口X的具体逻辑如下:

    整体逻辑是:

    a. 将git 和svn上的代码更新到本地

    b. 将文件复制到指定目录中

    c. 执行编译命令: 编译命令使用的是ccs的编译命令

    d. 判断编译是否成功,成功的话则将编译结果复制到指定目录中

    e. 获取源码目录的最新版本号及备注信息,并拼接成备注信息,将结果文件提交到svn上。

    先将其关键代码展示:

    // 操作git,使用的是org.eclipse.jgit  5.13.3.202401111512-r
    /**
         * 克隆仓库
         *
         * @throws Exception
         */
        public void cloneRep(boolean force) throws Exception {
            File targetDirectory = new File(getLocalPath());
            boolean exists = targetDirectory.exists();
            if (exists && force) {
                FileUtil.del(targetDirectory);
            } else if (exists) {
                return;
            }
            Git.cloneRepository()
                    .setURI(getRepUrl())
                    .setBranch(getBranch())
                    .setDirectory(targetDirectory)
                    .setCredentialsProvider(new UsernamePasswordCredentialsProvider(getUsername(), getPassword()))
                    .call();
        }
    
    
        /** 获取仓库版本 */
        public String getRepVersion(){
            File localFile = new File(getLocalPath());
            boolean exists = localFile.exists();
            if (!exists) {
                return "";
            }
            try (Git git = Git.open(localFile)) {
                final Iterable<RevCommit> revCommits = git.log().setMaxCount(1).call();
                final RevCommit revCommit = revCommits.iterator().next();
                final String commitDate = DateUtil.format(revCommit.getAuthorIdent().getWhen(), "yyyy-MM-dd HH:mm:ss");
                final String commitName = revCommit.getAuthorIdent().getName();
                return String.format("%s(%s)", commitName,commitDate);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            return "";
        }
    
        /** 更新仓库 */
        public void updateRep(boolean force) throws Exception {
            File localFile = new File(getLocalPath());
            boolean exists = localFile.exists();
            if (!exists) {
                cloneRep(force);
                return;
            }
            try (Git git = Git.open(localFile)) {
                if (force) {
                    // 撤销所有未提交的本地修改
                    git.reset()
                            .setMode(ResetCommand.ResetType.HARD)
                            .call();
                    // 删除未跟踪的文件和目录
                    git.clean()
                            .setCleanDirectories(true) // 递归清理子目录
                            .call();
                }
                // 设置凭据
                CredentialsProvider cp = new UsernamePasswordCredentialsProvider(getUsername(), getPassword());
                git.fetch()
                        .setCredentialsProvider(cp)
                        .call();
                git.pull()
                        .setRebase(true) // 默认情况下合并(merge),这里改为变基(rebase)
                        .setCredentialsProvider(cp)
                        .call();
            } catch (RepositoryNotFoundException e) {
                // 未找到仓库
                cloneRep(true);
            }
        }
    
// 操作 svn
static {
        DAVRepositoryFactory.setup();
        SVNRepositoryFactoryImpl.setup();
        FSRepositoryFactory.setup();
    }

    public void updateRep() throws Exception {
        updateRep(true);
    }

    public void updateRep(boolean force) throws Exception {
        log.info("updateRep");
        BasicAuthenticationManager authManager = new BasicAuthenticationManager(getUsername(), getPassword());
        SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(getRepUrl()));
        repository.setAuthenticationManager(authManager);
        File targetFile = new File(getLocalPath(), "\\");
        if (force && targetFile.exists()) {
            // 撤销本地修改
            SVNWCClient wcClient = SVNClientManager.newInstance(null, authManager).getWCClient();
            wcClient.doRevert(new File[]{targetFile}, SVNDepth.INFINITY, null);
        }
        // 检出
        SVNUpdateClient updateClient = SVNClientManager.newInstance(null, authManager).getUpdateClient();
        updateClient.doCheckout(repository.getLocation(), targetFile, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY, false);
    }

    public void commit(List<File> delFileList) throws Exception {
        commit("", delFileList);
    }

    public void commit(String commitMsg, List<File> delFileList) throws Exception {
        if (!isNeedCommit()) {
            log.info("不需要提交,直接跳过!");
            return;
        }
        BasicAuthenticationManager authManager = new BasicAuthenticationManager(getUsername(), getPassword());
        SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(getRepUrl()));
        repository.setAuthenticationManager(authManager);
        SVNCommitClient client = SVNClientManager.newInstance(null, authManager).getCommitClient();
        File[] pathsToCommit = {new File(getLocalPath())};
        List<SVNURL> delSvnUrlList = new ArrayList<>();
        if (delFileList != null && !delFileList.isEmpty()) {
            for (File file : delFileList) {
                SVNURL svnUrl = getSvnUrl(file);
                if (isURLExist(svnUrl)) {
                    delSvnUrlList.add(svnUrl);
                } else {
                    file.delete();
                }
            }
        }
        if (!delSvnUrlList.isEmpty()) {
            SVNURL[] array = delSvnUrlList.toArray(new SVNURL[0]);
          // 先把老的旧文件删除掉。
            client.doDelete(array, StrUtil.isBlank(commitMsg) ? getCommitMsg() : commitMsg);
        }
      // 添加新增加的文件
        SVNClientManager.newInstance(null, authManager).getWCClient()
                .doAdd(pathsToCommit, true, true, true, SVNDepth.INFINITY, true, false, true);
        SVNCommitInfo commitInfo = client.doCommit(pathsToCommit, false,
                StrUtil.isBlank(commitMsg) ? getCommitMsg() : commitMsg, false, true);
        log.info("Committed revision: {}", commitInfo.getNewRevision());
    }

    private boolean isURLExist(SVNURL url) {
        try {
            BasicAuthenticationManager authManager = new BasicAuthenticationManager(getUsername(), getPassword());
            SVNRepository svnRepository = SVNRepositoryFactory.create(url);
            svnRepository.setAuthenticationManager(authManager);
            SVNNodeKind nodeKind = svnRepository.checkPath("", -1);
            return nodeKind == SVNNodeKind.NONE ? false : true;
        } catch (SVNException e) {
            log.error("isURLExist error", e);
        }
        return false;
    }

    private SVNURL getSvnUrl(File file) throws SVNException {
        String svnUrl = StrUtil.replace(file.getAbsolutePath(), getRepLocalBasePath(), getRepUrl());
        svnUrl = svnUrl.replace("\\", "/");
        log.info("getSvnUrl: {}", svnUrl);
        return SVNURL.parseURIEncoded(svnUrl);
    }

/**
获取svn指定子目录的最后提交版本。
*/
    public long getRepVersion() {
        try {
            log.info("getRepVersion");
            BasicAuthenticationManager authManager = new BasicAuthenticationManager(getUsername(), getPassword());
            SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(getRepUrl()));
            log.info("getRepVersion repository.getLocation():{}", repository.getLocation().toString());
            repository.setAuthenticationManager(authManager);
            long version = repository.getLatestRevision();
            log.info("getRepVersion version:{}", version);
            File versionFile = new File(getRepLocalBasePath() + getSvnVersionPath());
            SVNStatus status = SVNClientManager.newInstance(null, authManager)
                    .getStatusClient().doStatus(versionFile, false);
            if (status != null) {
                version = status.getCommittedRevision().getNumber();
            }
            return version;
        } catch (Exception e) {
            log.error("getRepVersion error", e);
        }
        return -1;
    }

/**
获取svn指定版本的日志信息.
*/
    public String getLogInfo(long revision) {
        try {
            BasicAuthenticationManager authManager = new BasicAuthenticationManager(getUsername(), getPassword());
            SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(getRepUrl()));
            log.info("getRepVersion repository.getLocation():{}", repository.getLocation().toString());
            repository.setAuthenticationManager(authManager);
            log.info("getRepVersion version:{}", revision);
            File versionFile = new File(getRepLocalBasePath() + getSvnVersionPath());
            StringBuffer logInfoBuf = new StringBuffer();
            ISVNLogEntryHandler handler = logEntry -> {
                String logInfo = String.format("%s %s",
                        DateUtil.format(logEntry.getDate(), "yyyyMMddHH:mm:ss"),
                        logEntry.getMessage());
                logInfoBuf.append(logInfo);
                log.info("logInfo {}: {}", logEntry.getRevision(), logInfo);
            };
            SVNLogClient logClient = new SVNLogClient(authManager, null);
            logClient.doLog(new File[]{versionFile},
                    SVNRevision.create(revision), SVNRevision.create(revision),
                    true, true,
                    1, handler);
            return logInfoBuf.toString();
        } catch (Exception e) {
           log.error("getLogInfo error", e);
        }
        return "";
    }
# ccs编译命令
@echo off
set ccs_home=E:\programe\ccs124
set workspace=yyyy
set proj_home=xxxx

set eclipsec="%ccs_home%\ccs\eclipse\eclipsec"
set proj_name=zzz

rem rmdir /S /Q "%proj_home%"\Release
rem TortoiseProc.exe /command:remove /y /path:"%proj_home%\Release\"
 
 
 rmdir /S /Q "%workspace%"

 mkdir "%workspace%"

rem 导入项目
 "%eclipsec%" -noSplash -data "%workspace%" -application com.ti.ccstudio.apps.projectImport -ccs.location "%proj_home%" -ccs.renameTo "%proj_name%"  >> ./logs/gmakeLog_%date:~0,4%%date:~5,2%%date:~8,2%.log

rem 清空项目.
"%eclipsec%" -noSplash -data "%workspace%" -application com.ti.ccstudio.apps.projectBuild -ccs.projects "%proj_name%" -ccs.clean >> ./logs/gmakeLog_%date:~0,4%%date:~5,2%%date:~8,2%.log

rem 编译.
"%eclipsec%" -noSplash -data "%workspace%" -application com.ti.ccstudio.apps.projectBuild -ccs.projects "%proj_name%" -ccs.configuration Release >> ./logs/gmakeLog_%date:~0,4%%date:~5,2%%date:~8,2%.log
  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜗牛_snail

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值