- 浏览: 200876 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
nenyalanye_1:
没用过 下下来研究研究!
JSDT-Eclipse下的Javascript调试插件 -
noahweb:
datas = new BO.Datas();
entryA ...
Easyb初体验 -
noahweb:
easyb 如何定义一个list?结构如下,如果对BO.Dat ...
Easyb初体验 -
elemark:
很有用,受教了其中关于响应时间timeout的处理,帮了我的大 ...
Ext点滴 -
inritxihc:
哦哦,解决了我的疑惑。
div层遮盖flash(兼容浏览器)
一、理论篇:
持续集成鼓励尽量短周期内项目团队的代码提交,同时保证每次check in都不会损害我们的构建通过。它跟每日构建的区别就在于代码提交频率更高(一般为一个小时),构建的频率也更高,这样做的目的就是为了快速反馈,使得BUG越早被发现,并能以邮件或者消息(甚至短信)的形式快速反馈给开发人员,从而快速解决问题,并保证构建成功。
二、工具篇:
持续集成重在COC(Conversion Over Configuration:约定由于配置),这样选择合适的支持持续集成的工具就相当重要。庆幸的是我们有许多开源的选择,但是首先我们需要了解持续集成的实现架构:
从上图中我们看到,客户端提交代码更改到源代码仓库,CI服务器会检测到代码库的修改,它会检出代码,在本地构建,构建成功,会将构建结果反馈回客户端,同时可能将构建的可运行代码发布到WEB服务器上。
所以,我们就需要各个节点的工具支持:
对于SCM工具,我们的选择的开源工具有CVS、SVN等,这也没有特殊的取舍,就自己的爱好和公司的已有平台而定。我们这里假设使用SVN作为版本管理工具,它的中文站是:http://www.subversion.org.cn/ ;
对于构建工具,我们的选择的开源工具有Ant和Maven等,Ant通过一些内置的和扩展的Task来实现包括文件操作、编译、测试、代码检查、打包等操作,Eclipse默认提供了对Ant的支持。通过在build.xml中配置一系列相互依赖的任务来实现我们定义的构建过程。Maven是一个以项目为模型的构建,项目管理工具,注意它并不是为了替代Ant(同时支持运行Ant脚本),而是以另一种视觉提供了对软件生命周期的管理,它通过插件的方式提供了类似于Ant任务的功能,它的特色之处在于对项目依赖组件的统一管理,同时它的生成站点功能也是一个不错的特性,具体不再赘述,后面的构建我们会分别用Ant和Maven来说明。
对于持续集成工具,我们的选择的开源工具有CruiseControl(后面简称CC)和Hudson。BuildLoop是CC的核心, 这个BuildLoop包含插件支持,详细介绍可以参照附件中的电子书。CC的实施结构如下图所示:
为了完成上面的结构,CC提供的插件按照下图所示的流程完成构建:
通过对CC的cruisecontrol项目的配置,支持图形化显示checkstyle, pmd, findbugs, cobertura, javadoc等报告。同时CC-Config也提供了对项目的图形化配置,比较方便。
Hudson也提供了持续集成服务器的大多数功能,详细参考官方站点:https://hudson.dev.java.net/
三、实践篇:
我们模拟了两个项目,一个AntBasedCI是基于Ant构建的客户端应用程序,一个MavenBasedCI是基于Maven构建的Web应用程序,我们的SCM由SVN自带的Server提供,启动这个服务,我们需要在命令行运行: svnserve -d -r D:\repos 其中-r指定repository磁盘位置。
CC的项目配置:
<cruisecontrol> <project requiremodification="false" forceonly="false" name="MavenBasedCI"> <modificationset QuietPeriod="30"> <svn LocalWorkingCopy="${checkout.dir}\${project.name}" CheckExternals="false" UseLocalRevision="false" /> </modificationset> <schedule Interval="300"> <maven2 Goal="-e clean site install" MvnHome="D:\OpenSource\maven-2.0.4" PomFile="${checkout.dir}/${project.name}/pom.xml" ShowProgress="false" /> </schedule> <bootstrappers> <svnbootstrapper LocalWorkingCopy="${checkout.dir}\${project.name}" /> </bootstrappers> <listeners> <currentbuildstatuslistener File="${logs.dir}\${project.name}\status.txt" /> </listeners> <log> <merge Dir="${logs.dir}\${project.name}" /> <merge Dir="${checkout.dir}\${project.name}\target" Pattern="*.xml" /> </log> <publishers> <onsuccess> <artifactspublisher Dest="${artifact.dir}\${project.name}" File="${checkout.dir}\${project.name}\target\${project.name}-1.0-SNAPSHOT.war" /> </onsuccess> <artifactspublisher File="${checkout.dir}\${project.name}\target\${project.name}-1.0-SNAPSHOT.war" Dest="artifacts/${project.name}" /> <artifactspublisher Dir="${checkout.dir}\${project.name}\target\site" Dest="artifacts/${project.name}" /> </publishers> <property name="checkout.dir" value="${basedir}\checkout" /> <property name="logs.dir" value="${basedir}\logs" /> <property name="artifact.dir" value="${basedir}\artifacts" /> </project> <project requiremodification="false" forceonly="false" name="AntBasedCI"> <modificationset QuietPeriod="30"> <svn LocalWorkingCopy="${checkout.dir}\${project.name}" CheckExternals="false" UseLocalRevision="false" /> </modificationset> <schedule Interval="300"> <ant AntHome="D:\OpenSource\apache-ant-1.7.1" BuildFile="${checkout.dir}\${project.name}\build.xml" Target="all" /> </schedule> <bootstrappers> <svnbootstrapper LocalWorkingCopy="${checkout.dir}\${project.name}" /> </bootstrappers> <listeners> <currentbuildstatuslistener File="${logs.dir}\${project.name}\status.txt" /> </listeners> <log> <merge Dir="${logs.dir}\${project.name}" /> <merge Dir="${checkout.dir}\${project.name}\target" Pattern="*.xml" /> </log> <publishers> <onsuccess> <artifactspublisher Dest="${artifact.dir}\${project.name}" File="${checkout.dir}\${project.name}\target\${project.name}.jar" /> </onsuccess> <artifactspublisher Dest="artifacts/${project.name}" File="${checkout.dir}\${project.name}\target\${project.name}.jar" /> <artifactspublisher Dir="${checkout.dir}\${project.name}\target" Dest="artifacts/${project.name}" /> </publishers> </project> <dashboard /> <property name="basedir" value="E:\CI\ccworkspace" /> <property name="checkout.dir" value="${basedir}\checkout" /> <property name="logs.dir" value="${basedir}\logs" /> <property name="artifact.dir" value="${basedir}\artifacts" /> </cruisecontrol>
上面的配置是通过CC-Config图形配置自动生成的,包含我们的两个工程。
Ant配置build.xml
<?xml version="1.0" encoding="UTF-8"?> <project name="AntBasedCI" default="all"> <property name="default.target.dir" value="target" /> <property name="classes.dir" value="${default.target.dir}/classes" /> <property name="test.classes.dir" value="${default.target.dir}/test-classes" /> <property name="test.report.dir" value="${default.target.dir}/test-reports" /> <property name="lib.dir" value="${basedir}/lib" /> <property name="javadoc.dir" value="${default.target.dir}/apidocs" /> <property name="source.dir" value="src" /> <property name="test.source.dir" value="test" /> <property name="test.pattern" value="**/**Test.java" /> <!-- Coverage reports are deposited into these directories --> <property name="cobertura.dir" value="${default.target.dir}/cobertura"/> <!-- Instrumented classes are deposited into this directory --> <property name="instrumented.dir" value="instrumented" /> <path id="cobertura.classpath"> <fileset dir="${lib.dir}"> <include name="*.jar" /> </fileset> </path> <taskdef classpathref="cobertura.classpath" resource="tasks.properties"/> <target name="clean"> <delete dir="${classes.dir}"/> <delete dir="${test.classes.dir}"/> <delete dir="${default.target.dir}"/> </target> <target name="init" depends="clean"> <mkdir dir="${classes.dir}" /> <mkdir dir="${test.classes.dir}" /> <mkdir dir="${javadoc.dir}" /> <mkdir dir="${default.target.dir}"/> <mkdir dir="${instrumented.dir}"/> <path id="build.classpath"> <fileset dir="${lib.dir}"> <include name="**/*.jar" /> </fileset> <fileset dir="${default.target.dir}"> <include name="**/*.jar" /> </fileset> </path> </target> <target name="compile-source" depends="init" description="compiles all .java files in source directory "> <javac destdir="${classes.dir}" srcdir="${source.dir}" classpathref="build.classpath" /> </target> <target name="instrument" depends="compile-source"> <delete file="cobertura.ser"/> <delete dir="${instrumented.dir}" /> <!--Instrument the application classes, writing the instrumented classes into ${build.instrumented.dir}.--> <cobertura-instrument todir="${instrumented.dir}"> <ignore regex="org.apache.log4j.*" /> <fileset dir="${classes.dir}"> <!-- Instrument all the application classes, but don't instrument the test classes.--> <include name="**/*.class" /> <exclude name="**/*Test.class" /> </fileset> </cobertura-instrument> </target> <target name="jar" depends="instrument"> <jar jarfile="${default.target.dir}/${ant.project.name}.jar" basedir="${classes.dir}" /> </target> <target name="compile-tests" depends="jar" description="compiles all .java files in test directory "> <javac destdir="${test.classes.dir}" srcdir="${test.source.dir}" classpathref="build.classpath" /> </target> <target name="javadoc" depends="init"> <javadoc author="true" charset="gbk" classpathref="build.classpath" destdir="${javadoc.dir}" version="true" use="true" sourcepath="${source.dir}"></javadoc> </target> <target name="test" depends="compile-tests" description="runs JUnit tests"> <mkdir dir="${test.report.dir}" /> <junit dir="${basedir}" printSummary="on" fork="true" haltonfailure="true"> <sysproperty key="basedir" value="${basedir}" /> <formatter type="xml" /> <classpath> <path refid="build.classpath" /> <pathelement path="${test.classes.dir}" /> <pathelement path="${classes.dir}" /> </classpath> <batchtest todir="${test.report.dir}"> <fileset dir="${test.source.dir}"> <include name="${test.pattern}" /> </fileset> </batchtest> </junit> </target> <target name="coverage-check"> <cobertura-check branchrate="40" totallinerate="100" /> </target> <target name="coverage-report"> <cobertura-report srcdir="${source.dir}" destdir="${cobertura.dir}" format="html" /> </target> <target name="alternate-coverage-report"> <!-- Generate a series of HTML files containing the coverage data in a user-readable form using nested source filesets. --> <cobertura-report destdir="${cobertura.dir}"> <fileset dir="${source.dir}"> <include name="**/*.java"/> </fileset> </cobertura-report> </target> <target name="coverage" depends="jar,instrument,test,coverage-report,alternate-coverage-report"/> <target name="pmd" depends="test"> <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="build.classpath"/> <pmd> <ruleset>rulesets/basic.xml</ruleset> <ruleset>rulesets/braces.xml</ruleset> <ruleset>rulesets/javabeans.xml</ruleset> <ruleset>rulesets/unusedcode.xml</ruleset> <ruleset>rulesets/strings.xml</ruleset> <ruleset>rulesets/design.xml</ruleset> <ruleset>rulesets/coupling.xml</ruleset> <ruleset>rulesets/codesize.xml</ruleset> <ruleset>rulesets/imports.xml</ruleset> <ruleset>rulesets/naming.xml</ruleset> <formatter type="xml" toFile="${default.target.dir}/pmd_report.xml" /> <fileset dir="${source.dir}"> <include name="**/*.java" /> </fileset> </pmd> </target> <target name="findbugs" depends="jar"> <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" classpathref="build.classpath" /> <findbugs classpathref="build.classpath" pluginlist="${lib.dir}/coreplugin-1.0.jar" output="xml" outputFile="${default.target.dir}/findbugs.xml"> <sourcePath path="${source.dir}" /> <class location="${default.target.dir}/${ant.project.name}.jar" /> </findbugs> </target> <target name="all" depends="coverage,pmd,findbugs,javadoc" /> </project>
从上面的配置我们看到这个构建包括:编译、测试、测试覆盖率统计、代码检查、BUG查找、生成Javadoc和打包。
Maven的配置pom.xml
<?xml version="1.0" encoding="GBK"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.samueli.webapp</groupId> <artifactId>MavenBasedCI</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>MavenBasedCI</name> <description>持续集成测试项目</description> <url>http://www.samueli.com</url> <licenses> <license> <name>Fake License</name> <url>http://127.0.0.1:8080/dc-license.txt</url> <distribution>repo</distribution> </license> </licenses> <!-- 问题管理 --> <issueManagement> <system>jira</system> <url>http://169.254.11.166:8088/secure/Dashboard.jspa</url> </issueManagement> <!-- 源代码管理 --> <scm> <connection>scm:svn://127.0.0.1/repos/MavenBasedCI</connection> <developerConnection> scm:svn://127.0.0.1/repos/MavenBasedCI </developerConnection> <tag>head</tag> <url>svn://127.0.0.1/repos/MavenBasedCI</url> </scm> <!-- 开发人员以及角色定义 --> <developers> <developer> <name>aaa</name> <id>aaa</id> <email>aaa@samueli.com</email> <organization>samueli</organization> <roles> <role>PM</role> </roles> </developer> <developer> <name>bbb</name> <id>bbb</id> <email>bbb@samueli.com</email> <organization>samueli</organization> <roles> <role>设计人员</role> <role>开发人员</role> </roles> </developer> </developers> <!-- 依赖定义 --> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.3</version> </dependency> </dependencies> <!--依赖版本管理--> <dependencyManagement /> <organization> <name>XXX有限责任公司</name> <url>http://www.samueli.com</url> </organization> <!--项目模块管理--> <modules /> <build> <plugins> <plugin> <artifactId>maven-site-plugin</artifactId> <configuration> <locales>zh_CN</locales> <outputEncoding>GBK</outputEncoding> </configuration> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> <encoding>GBK</encoding> </configuration> </plugin> <plugin> <artifactId>maven-checkstyle-plugin</artifactId> <configuration> <outputEncoding>GBK</outputEncoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> <configuration> <linkXref>true</linkXref> <sourceEncoding>GBK</sourceEncoding> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.0</version> <configuration> <instrumentation> <excludes> <exclude> com/samueli/MavenBasedCI/util/*.class </exclude> </excludes> </instrumentation> <check> <haltOnFailure>true</haltOnFailure> <totalLineRate>40</totalLineRate> <totalBranchRate>100</totalBranchRate> </check> <outputEncoding>GBK</outputEncoding> </configuration> <executions> <execution> <id>clean</id> <goals> <goal>clean</goal> </goals> </execution> <execution> <id>check</id> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> </plugins> <extensions> <extension> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-ftp</artifactId> <version>1.0-alpha-6</version> </extension> </extensions> </build> <distributionManagement> <repository> <id>ftp-repository</id> <url>ftp://192.168.3.241</url> </repository> <site> <id>lmss.site</id> <name>MavenBasedCI</name> <url>file:///Y:\</url> </site> </distributionManagement> <!--各种报告--> <reporting> <plugins> <plugin> <artifactId>maven-site-plugin</artifactId> <configuration> <locales>zh_CN</locales> <outputEncoding>GBK</outputEncoding> </configuration> </plugin> <plugin> <artifactId>maven-surefire-report-plugin</artifactId> <configuration> <showSuccess>false</showSuccess> <outputEncoding>GBK</outputEncoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jxr-plugin</artifactId> <configuration> <outputEncoding>GBK</outputEncoding> <inputEncoding>GBK</inputEncoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <configuration> <aggregate>true</aggregate> <charset>UTF16</charset> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <configuration> <rulesets> <ruleset>/rulesets/basic.xml</ruleset> <ruleset>/rulesets/imports.xml</ruleset> <ruleset>/rulesets/unusedcode.xml</ruleset> <ruleset>/rulesets/finalizers.xml</ruleset> </rulesets> <outputEncoding>GBK</outputEncoding> <linkXref>true</linkXref> <excludes> <exclude> com/samueli/MavenBasedCI/util/*.java </exclude> </excludes> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <configuration> <configLocation>lmss_checks.xml</configLocation> <outputEncoding>GBK</outputEncoding> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>taglist-maven-plugin</artifactId> <configuration> <tags> <tag>TODO</tag> <tag>@todo</tag> <tag>FIXME</tag> <tag>XXX</tag> </tags> <outputEncoding>GBK</outputEncoding> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.0</version> <configuration> <outputEncoding>GBK</outputEncoding> </configuration> </plugin> <!-- <plugin>--> <!-- <groupId>org.codehaus.mojo</groupId>--> <!-- <artifactId>changes-maven-plugin</artifactId>--> <!-- </plugin>--> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>changelog-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> <configuration> <xmlOutput>true</xmlOutput> </configuration> </plugin> </plugins> </reporting> </project>
Maven详细的站点生成可以参考这里:http://www.duduwolf.com/wiki/2008/766.html
Maven生成的站点例子:
CC生成的集成报告截图如下:
四、总结篇:
通过上面的简单介绍,我们基本掌握了持续集成的目的和基本理论,在Martin Fowler的文章中提到了一些最佳实践也值得参考。当然持续集成是一个在实践中不断发展和完善的过程,对于一个团队而言,引入持续集成对于提高开发效率和规范开发过程是必需的,不过在整个持续集成中,我们信赖的依据就是构建,其中的单元测试可靠性就会有一定的要求,这样对于我们开发人员,如何保证写出高质量的单元测试便是一个挑战,TDD是一个不错的实践,它完全从需求出发,逐步完善测试用例,不断减少与需求的偏差来尽量满足需求。同时引入测试覆盖率也利于我们审查我们的单元测试。CC提供的统一出口的各种报告和图表,可以更加直观和快捷的从整体上把握我们代码在构建中表现出来的健壮性(代码检查)和满足需求性(单元测试通过率、测试覆盖率),同时对于出现的问题,能够责任到人,快速反馈也是很有利于问题的解决,对于持续集成的学习刚刚开始,错误偏颇在所难免,越是深入的学习,越会有更多的感悟和思考。
五、参考:
1、Martin Fowler的文章
原文:http://martinfowler.com/articles/continuousIntegration.html
翻译:http://dev.csdn.net/develop/article/12/12286.shtm
2、Juven的一篇原创文章
http://juvenshun.spaces.live.com/blog/cns!CF7D1BC903C111E1!284.entry
3、IBM DW上的一篇教程,以Hudson为例
https://www6.software.ibm.com/developerworks/cn/education/java/j-cq11207/index.html
4、满江红开源上提供的CruiseControl教程下载
http://www.xiaxin.net/blog/OpenDoc-CruiseControl.zip
5、IBM DW上的另外一篇文章讲解如何实现持续集成
http://www.ibm.com/developerworks/cn/rational/rationaledge/content/nov05/lee/
评论
1.任务compile-source中的javac必须增加“debug="yes"”的属性
2.在任务test的junit中,必须在配置<classpath>前插入节点“<classpath location="${instrumented.dir}" />”
具体原因分析可以参考我写的文章:
持续集成,理论,工具,最佳实践,缺一不可。
相关推荐
总结来说,持续集成是一种提高软件开发效率和软件质量的有效方法。它要求开发人员频繁地将代码集成到主干,并通过自动化测试确保代码的稳定性。与之相关联的持续交付和持续部署,则是在持续集成的基础上,进一步确保...
### 持续集成框架整合说明 #### 一、引言 随着软件开发流程的不断演化,持续集成(CI)已成为提高软件质量和开发效率的重要手段之一。本文将详细介绍如何利用Jenkins、Maven以及Sonar等工具搭建一套完整的持续集成...
代码持续集成是一种开发实践,要求开发人员频繁地将他们的更改合并到主分支,通常每次提交后都会自动运行构建和测试。这有助于尽早发现和解决问题,提高软件质量。实施持续集成的关键在于自动化,包括自动化构建、...
### Jenkins 持续集成部署概述 #### 一、持续集成的基本概念与目的 **持续集成(Continuous Integration, CI)**是一种软件开发实践,要求团队成员频繁地将他们的工作成果集成到共享的主干中,通常每天至少集成一...
总结来说,Jenkins作为强大的持续集成工具,能够帮助团队实现高效的开发流程,通过自动化减少错误,提高软件质量和开发速度。结合正确的持续集成原则和实践,Jenkins能够成为开发团队的得力助手。
**Jenkins持续集成工具** Jenkins是一款开源的持续集成(Continuous Integration, CI)服务器,它旨在通过自动化构建、测试和部署软件,以提高开发效率和软件质量。在Java平台上运行,Jenkins支持多种语言和框架的...
【持续集成工具之Hudson】 持续集成(Continuous Integration, CI)是一种软件开发实践,它强调开发者频繁地将代码更改合并到共享存储库中,并通过自动化构建和测试来快速发现并解决问题。CI的主要目的是减少集成...
- **总结**:通过对软件工程中的敏捷开发与持续集成的学习,我们了解到这两种方法论对于提升软件开发效率和质量的重要性。敏捷开发侧重于灵活适应变化,持续集成则强调自动化构建和测试,两者的结合为现代软件开发...
总结而言,自动化测试和持续集成是现代软件开发中不可或缺的组成部分。它们不仅能够提高软件的质量和开发效率,还能够帮助团队更好地管理项目风险。随着技术的发展,这些领域的工具和技术也在不断地进步和完善,因此...
总结来说,Jenkins+Maven+SVN+Tomcat的持续集成方案,能够简化网站部署流程,降低错误率,提高团队协作效率。通过自动化,开发者可以更快地获得反馈,及时修复问题,从而加速产品的迭代和发布。在实践中,不断优化...
Hudson是一款开源的持续...总结来说,Hudson作为一款强大的持续集成工具,其安装和配置过程相对简单,但功能强大,能够有效提升开发效率和代码质量。通过合理配置,Hudson可以帮助团队实现快速迭代和稳定的软件发布。
总结来说,Jenkins是持续集成领域的明星工具,它通过自动化流程降低了开发的复杂性,提高了软件的可靠性和迭代速度。通过合理的配置和实践,Jenkins可以帮助任何团队实现高效、可靠的持续集成环境。
### Jenkins——开源的持续集成与持续部署工具 #### 一、概述 Jenkins 是一款备受推崇的开源持续集成(CI)和持续部署(CD)工具,最初源自于 Hudson,使用 Java 语言编写,具备跨平台运行的能力。它可以独立运行...
Jenkins 提供了一个统一的框架,使得持续集成变得更加简单。其功能强大且灵活性高,支持多种插件,能够满足不同类型的项目需求。 #### 二、环境搭建及配置 ##### 2.1 Jenkins 安装 本文作者推荐使用的 Jenkins ...
### 京峰教育Jenkins自动部署与持续集成详解 #### 一、引言 随着互联网技术的快速发展,软件系统的更新迭代越来越频繁,这对企业的运维管理提出了更高的要求。传统的手动部署方式不仅耗时耗力,还容易出现人为失误...
本篇文章将重点围绕“系统集成项目管理工程师下午案例分析简单题要点”进行详细的解读和知识总结。 首先,我们要明确系统集成项目管理的核心概念。系统集成是指将不同的硬件、软件、网络等信息技术产品结合,形成一...
总结,Nightwatch.js作为Node.js生态系统中的一个强大工具,为Web应用程序的自动化测试和持续集成提供了便利。它简化了测试脚本的编写,支持多种浏览器,并且可以无缝融入现有的开发工作流,极大地提高了测试效率和...