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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio开发之路(八)Spinner样式设置

一、需求 白色背景显示下拉框按钮 问题&#xff1a; 设置Spinner的背景可以通过设置background&#xff1a; android:background"color/white",但是一旦设置了这个值&#xff0c;右侧的下拉按钮就会消失 方法一、自定义一个style&#xff08;不成功&#xff09; …

大模型推理框架Vllm和TensorRT-LLM在ChatGLM2-6B模型的推理速度对比

目录 一、框架的特点简介 1、vllm pagedAttention Continuous batching 2、TensorRT-LLM WOQ——W4A16、W8A16 SQ——SmoothQuant AWQ——Activation-aware Weight Quantization 二、web推理服务 vllm_service tensortllm_service 三、推理速度对比 1、非业务数据 …

第48期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

游戏陪玩系统app

游戏陪玩系统APP为用户提供了一个便捷的平台&#xff0c;让他们能够轻松找到合适的陪玩者&#xff0c;一同享受游戏的乐趣。以下是对您提到的功能的详细解释&#xff1a; 游戏约玩&#xff1a; 在陪玩APP上&#xff0c;用户可以浏览陪玩者的信息&#xff0c;包括他们的游戏技能…

用Excel做一个功能完备的仓库管理系统

1 基本设计思路 用到的Excel技术&#xff1a;sumif, vlookup, 表格(table)。基本思路&#xff1a;在有基础的商品、仓库等信息的情况下&#xff0c;对商品的每一个操作都有对应的单据&#xff0c;然后再汇总统计。标识&#xff1a;为了在不同的维度统计数量&#xff0c;各单据…

【七】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;所以按照…

Hive服务详解

Hive服务 HiveServer2、Hive Metastore 服务服务共同构成了 Hive 生态系统中的核心功能&#xff0c;分别负责管理元数据和提供数据查询服务&#xff0c;为用户提供了一个方便、高效的方式来访问和操作存储在 Hive 中的数据。 1. Hive 查询服务&#xff08;HiveServer2&#xf…

jmeter之连接MySQL数据库

jmeter连接mysql数据库 mysql官网下载地址&#xff1a;MySQL :: Download Connector/J 步骤如下&#xff1a; 1、下载mysql的jar包放入到jmeter的lib/ext下&#xff0c;然后重启jmeter 链接: https://pan.baidu.com/s/1rRrMQKnEuKz8zOUfMdMHFg?pwdawfc 提取码: awfc 2、配置…

构建NodeJS库--前端项目的打包发布

1. 前言 学习如何打包发布前端项目&#xff0c;需要学习以下相关知识&#xff1a; package.json 如何初始化配置&#xff0c;以及学习npm配置项&#xff1b; 模块类型type配置&#xff0c; 这是nodejs的package.json的配置main 入口文件的配置 webpack 是一个用于现代 JavaSc…

ElasticSearch总结二

正向索引和倒排索引&#xff1a; 正向索引&#xff1a; 比方说我这里有一张数据库表&#xff0c;那我们知道对于数据库它一般情况下都会基于i d去创建一个索引&#xff0c;然后形成一个b树。 那么你根据i d进行检索的速度&#xff0c;就会非常的快&#xff0c;那么这种方式的…

Cesium之加载GeoServer或geowebcache的WMTS服务

文章目录 Cesium加载GeoServer的WMTS关键代码WMTS服务地址获取核心参数获取 Cesium加载GeoServer的WMTS关键代码 Cesium之加载GeoServer或geowebcache的WMTS服务关键代码如下 var url2"http://localhost:8090/geowebcache/service/wmts/rest/arcgis_com/{style}/{TileMat…

在excel中,如何在一个表中删除和另一个表中相同的数据?

现在有A表&#xff0c;是活动全部人员的姓名和学号&#xff0c;B表是该活动中获得优秀人员的姓名和学号&#xff0c; 怎么提取没有获得优秀人员的名单&#xff1f; 这里提供两个使用excel基础功能的操作方法。 1.条件格式自动筛选 1.1按住Ctrl键&#xff0c;选中全表中的姓…

的记忆:pandas(实在会忘记,就看作是一个 Excel 表格,或者是 SQL 表,或者是字典的字典。)

pandas 是一个开源的 Python 数据分析库&#xff0c;它提供了快速、灵活和富有表现力的数据结构&#xff0c;旨在使“关系”或“标记”数据的“快速分析、清洗和转换”变得既简单又直观。pandas 非常适合于数据清洗和转换、数据分析和建模等任务。以下是 pandas 的基本概念和主…

用 LM Studio 1 分钟搭建可在本地运行大型语言模型平台替代 ChatGPT

&#x1f4cc; 简介 LM Studio是一个允许用户在本地离线运行大型语言模型&#xff08;LLMs&#xff09;的平台&#xff0c;它提供了一种便捷的方式来使用和测试这些先进的机器学习模型&#xff0c;而无需依赖于互联网连接。以下是LM Studio的一些关键特性&#xff1a; 脱机&am…

C++笔记:C++中的重载

重载的概念 一.函数重载 代码演示例子&#xff1a; #include<iostream> using namespace std;//函数名相同&#xff0c;在是每个函数的参数不相同 void output(int x) {printf("output int : %d\n", x);return ; }void output(long long x) {printf("outp…

RTU遥测终端为城市排水安全保驾护航!

近年来&#xff0c;全球气候变迁与城市化进程不断加速&#xff0c;导致强降雨事件频发&#xff0c;道路低洼地带、下穿式立交桥和隧道等区域在暴雨中常易积水&#xff0c;严重阻碍了人民的出行&#xff0c;甚至危及生命与财产安全。而传统的排水管网管理方式已难以适应现代城市…

mybatis的使用技巧8——联合查询union和union all的区别和用法

在实际项目开发中&#xff0c;会经常联合查询结构相似的多张数据表&#xff0c;使用union关键字就只需要一次sql操作&#xff0c;而无需执行多次查询并通过代码逻辑合并处理&#xff0c;减少了大量繁琐的操作&#xff0c;最重要的是还能通过可选的all关键字筛选重复的数据。 1…

数据结构基础:链表操作入门

数据结构基础&#xff1a;链表操作入门 数据结构基础&#xff1a;链表操作入门链表的基本概念链表的基本操作输出链表插入节点删除节点查找值 完整的链表操作示例结语 数据结构基础&#xff1a;链表操作入门 在计算机科学中&#xff0c;数据结构是组织和存储数据的方式&#x…

海康Visionmaster-常见问题排查方法-启动失数

问题2&#xff1a;VM无法启动&#xff0c;报错&#xff1a;参数错误&#xff1b;  问题原因&#xff1a;客户电脑环境异常导致代理启动失败。  解决方法&#xff1a;安装运行时库&#xff0c;并测试代理能否正常启动,步骤如下&#xff1a; ① 尝试双击代理进程&#xff…

WPF 6 命令

命令 创建一个按钮&#xff0c;新建一个事件&#xff0c;按住F12 就可以添加业务代码 运行代码 此时希望UI与后台代码分离&#xff0c;互不影响 此时新建一个MainViewModel类&#xff0c;来保存业务代码 Icommand 是所有command的父类接口 新建一个command来实现这个接口…