CCS项目持续集成

news2024/10/7 10:21:22

​ 因工作需要,用户提出希望可以做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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1618865.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷粒商城实战(017 业务-单点登录)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第231p-第p235的内容 介绍 单点登录&#xff08;Single Sign-On&#xff0c;SSO&#xff09;是一种身份验证服务&#xff0c;允许用户使用一组凭…

程序猿成长之路之数据挖掘篇——朴素贝叶斯

朴素贝叶斯是数据挖掘分类的基础&#xff0c;本篇文章将介绍一下朴素贝叶斯算法 情景再现 以挑选西瓜为例&#xff0c;西瓜的色泽、瓜蒂、敲响声音、触感、脐部等特征都会影响到西瓜的好坏。那么我们怎么样可以挑选出一个好的西瓜呢&#xff1f; 分析过程 既然挑选西瓜有多个…

【七】jmeter5.5+influxdb2.0+prometheus+grafana

参考文章&#xff1a;https://blog.csdn.net/wenxingchen/article/details/126892890 https://blog.csdn.net/Zuo19960127/article/details/119726652 https://blog.csdn.net/shnu_cdk/article/details/132182858 promethus参考 由于自己下载的是infuldb2.0&#xff0c;所以按照…

【视频异常检测】Open-Vocabulary Video Anomaly Detection 论文阅读

Open-Vocabulary Video Anomaly Detection 论文阅读 AbstractMethod3.1. Overall Framework3.2. Temporal Adapter Module3.3. Semantic Knowledge Injection Module3.4. Novel Anomaly Synthesis Module3.5. Objective Functions3.5.1 Training stage without pseudo anomaly …

【webrtc】Chrome和Firefox在SDP协商过程中,针对localhost的不同处理

内网下chrome端webrtc协商失败 现象 我有一个webrtc服务器在局域网内&#xff0c;使用chrome浏览器访问时&#xff0c;发现webrtc在做媒体协商时失败。 具体表现是&#xff0c;在交换sdp后&#xff0c;ice的状态是oniceconnectionstatechange: failed 但是换成Firefox浏览器…

【Linux】文件目录及路径表示

1. Linux目录结构 在 Linux 系统中&#xff0c;有几个目录是比较重要的&#xff0c;平时需要注意不要误删除或者随意更改内部文件。 /etc&#xff1a; 这个是系统中的配置文件&#xff0c;如果更改了该目录下的某个文件可能会导致系统不能启动。 /bin, /sbin, /usr/bin, /usr…

黄金行情下跌有投资机会吗?

尽管黄金价格的波动常常引起投资者的高度关注&#xff0c;但行情的下跌未必只是警讯&#xff0c;亦可能蕴藏着某些难得的投资机会。总之&#xff0c;答案是肯定的——在黄金行情下跌时&#xff0c;依旧有适宜的投资机会&#xff0c;只是这需要投资者具备相应的应对知识和策略。…

是德软件89600 RFID使用笔记

文章目录 1、进入RFID软件&#xff1a;2、RFID软件解调设置项3、如何查看一段指令数据 本文是日常工作的笔记分享。 lauch VSA&#xff08;矢量频谱分析&#xff09;后会出现以下界面&#xff1a; 当然这是因为频谱仪的输入有信号才显示如下&#xff1a; 否则就显示频谱仪的噪…

【书生浦语第二期实战营学习笔记作业(七)】

课程文档&#xff1a;https://github.com/InternLM/Tutorial/blob/camp2/opencompass/readme.md 课程作业&#xff1a;https://github.com/InternLM/Tutorial/blob/camp2/opencompass/homework.md OpenCompass 大模型评测实战 1. 大模型的评测1.1 大模型评测如何促进发展1.2 大…

WebSocket的原理、作用、常见注解和生命周期的简单介绍,附带SpringBoot示例

文章目录 WebSocket是什么WebSocket的原理WebSocket的作用全双工和半双工客户端【浏览器】API服务端 【Java】APIWebSocket的生命周期WebSocket的常见注解SpringBoot简单代码示例 WebSocket是什么 WebSocket是一种 通信协议 &#xff0c;它在 客户端和服务器之间建立了一个双向…

线性代数 --- 矩阵的对角化以及矩阵的n次幂

矩阵的对角化以及矩阵的n次幂 &#xff08;特征向量与特征值的应用&#xff09; 前言&#xff1a; 在上一篇文章中&#xff0c;我记录了学习矩阵的特征向量和特征值的学习笔记&#xff0c;所关注的是那些矩阵A作用于向量x后&#xff0c;方向不发生改变的x(仅有尺度的缩放)。线…

跟着野火从零开始手搓emWin(1)初识emWin

PS&#xff1a;在嵌入式领域&#xff0c;本人认为QT的应用范围和性能几乎吊打市面上所有的GUI工具。但是本人之所以学习emWin&#xff0c;是因为自己有些微不足道的小想法&#xff0c;需要通过它来实现。但是QT有点吃硬件的配置&#xff0c;为了MCU专门发行的QT我又懒得去弄&am…

mac电脑搭建vue项目(下篇)

第三步&#xff1a;安装npm &#xff08;1&#xff09;执行以下命令安装cnpm淘宝镜像 sudo npm install -g cnpm --registryhttps://registry.npmmirror.com &#xff08;2&#xff09;执行命令cnpm -v查看版本信息&#xff0c;结果说找不到cnpm命令 &#xff08;3&#xff…

智慧校园:大数据助力校情分析

随着信息技术的快速发展&#xff0c;数据信息资源以井喷的姿态涌现。数据信息的大量涌现给人们带来丰富的数据信息资源&#xff0c;但面对海量的信息资源时&#xff0c;加大了人们对有效信息资源获取的难度&#xff0c;数据挖掘技术正是这一背景下的产物&#xff0c;基于数据挖…

【003_音频开发_基础篇_Linux进程通信(20种你了解几种?)】

003_音频开发_基础篇_Linux进程通信&#xff08;20种你了解几种&#xff1f;) 文章目录 003_音频开发_基础篇_Linux进程通信&#xff08;20种你了解几种&#xff1f;)创作背景Linux 进程通信类型fork() 函数fork() 输出 2 次fork() 输出 8 次fork() 返回值fork() 创建子进程 方…

elasticsearch-8.1.0安装记录

目录 零、版本说明一、安装二、使用客户端访问 零、版本说明 centos [rootnode1 ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)elasticsearch elasticsearch-8.1.0-linux-x86_64一、安装 systemctl stop firewalld.servicesystemctl disable firewal…

【国产替代】航空电子通信总线航空电子通信总线产品为MIL-STD-1553和ARINC 429等协议提供原生支持

航空电子通信总线 航空电子通信总线产品为MIL-STD-1553和ARINC 429等协议提供原生支持。这些产品用于进行航空电子应用所需的开发、生产和系统测试。 PXIe&#xff0c;2通道PXI ARINC-664接口模块 AIM ARINC-664具有板载处理器&#xff0c;可自动处理所有与协议相关的活动&…

python绘图时渐变的处理——以一个扇形图的渐变为例

python绘图时渐变的处理——以一个扇形图的渐变为例 使用matplotlib绘制扇形的圆环 from matplotlib.patches import Wedge wedgeWedge((0,0),1,0,60,width0.3,colorred) wedge.set_edgecolor(k) fig,axplt.subplots(1,1) ax.add_patch(wedge) # 设置坐标轴的比例 plt.axis(e…

DaPy:实现数据分析与处理

DaPy&#xff1a;实现数据分析与处理 DaPy是一个用于数据分析和处理的Python库&#xff0c;它提供了一系列强大的工具和功能&#xff0c;使开发者能够高效地进行数据清洗、转换和分析。本文将深入解析DaPy库的特点、功能以及使用示例&#xff0c;帮助读者了解如何利用DaPy库处理…

数据库之数据库恢复技术思维导图+大纲笔记

大纲笔记&#xff1a; 事务的基本概念 事务 定义 用户定义的一个数据库操作系列&#xff0c;这些操作要么全做&#xff0c;要么全不做&#xff0c;是一个不可分割的基本单位 语句 BEGIN TRANSACTION 开始 COMMIT 提交&#xff0c;提交事务的所有操作 ROLLBACK 回滚&#xff0c…