Java 프로젝트 (ant, cvs, hudson)의 빌드 및 버전 번호 매기기
Java 프로젝트에서 체계적인 빌드 번호 매기기 및 버전 번호 관리에 대한 현재 모범 사례는 무엇입니까? 구체적으로 특별히:
분산 개발 환경에서 빌드 번호를 체계적으로 관리하는 방법
소스에서 버전 번호를 유지하는 방법 / 런타임 애플리케이션에서 사용 가능한 방법
소스 리포지토리와 올바르게 통합하는 방법
버전 번호와 저장소 태그를보다 자동으로 관리하는 방법
지속적인 빌드 인프라와 통합하는 방법
사용 가능한 많은 도구가 있으며 개미 (우리가 사용하는 빌드 시스템)에는 빌드 번호를 유지 관리하는 작업이 있지만 CVS, svn 또는 이와 유사한 것을 사용하여 여러 명의 동시 개발자와 함께 관리하는 방법은 명확하지 않습니다 .
[편집하다]
몇 가지 좋고 유용한 부분 또는 구체적인 답변이 아래에 나와 있으므로 그 중 일부를 요약하겠습니다. 이것에 대한 강력한 "모범 사례"가 아니라 중복되는 아이디어 모음이있는 것처럼 들립니다. 아래에서 내 요약과 사람들이 후속 조치로 답변을 시도 할 수있는 몇 가지 결과적인 질문을 찾으십시오. [스택 오버플로가 처음입니다 ... 내가 잘못하고있는 경우 의견을 입력 해주세요.]
SVN을 사용하는 경우 특정 체크 아웃의 버전 관리가 제공됩니다. 빌드 번호 매기기는이를 이용하여 특정 체크 아웃 / 수정을 식별하는 고유 한 빌드 번호를 만들 수 있습니다. [우리가 레거시 이유로 사용하고있는 CVS는 이러한 수준의 통찰력을 제공하지 않습니다. 태그를 사용하여 수동으로 개입하면 도움이됩니다.]
maven을 빌드 시스템으로 사용하는 경우 SCM에서 버전 번호를 생성하고 릴리스를 자동으로 생성하기위한 릴리스 모듈이 지원됩니다. [우리는 여러 가지 이유로 maven을 사용할 수 없지만 이것은 가능한 사람들에게 도움이됩니다. [ 마르첼로-도덕 감사합니다 ]]
당신이 사용하는 경우 개미를 빌드 시스템으로, 다음과 같은 작업 설명 캔 도움은 여러 가지 방법으로 빌드로 접을 수있는 Java .properties의 파일 캡처 빌드 정보를 생산하고 있습니다. [우리는 marty-lamb 덕분에 hudson 파생 정보를 포함하도록이 아이디어를 확장했습니다 ].
Ant 및 Maven (및 hudson 및 cruise 컨트롤)은 빌드 번호를 .properties 파일 또는 .txt / .html 파일로 가져 오는 쉬운 방법을 제공합니다. 이 "안전한"부분이 의도적으로 또는 실수로 변조되지 않도록하기에 충분합니까? 빌드시 "versioning"클래스로 컴파일하는 것이 더 낫습니까?
주장 : 빌드 넘버링은 hudson 과 같은 지속적인 통합 시스템에서 정의 / 제정되어야합니다 . [ Marcelo-morales 에게 감사드립니다 ] 우리는이 제안을 받아 들였지만 , 릴리즈 엔지니어링 질문 을 깨뜨 렸습니다 : 릴리즈는 어떻게 발생합니까? 릴리스에 여러 빌드 번호가 있습니까? 다른 릴리스의 빌드 번호 간에는 의미있는 관계가 있습니까?
질문 : 빌드 번호의 목표는 무엇입니까? 품질 관리에 사용됩니까? 어떻게? 개발자가 주로 개발 중에 여러 빌드를 명확하게하거나 QA가 최종 사용자가 얻은 빌드를 결정하기 위해 더 많이 사용합니까? 재현성이 목표라면 이론적으로 이것은 릴리스 버전 번호가 제공해야하는 것입니다. 왜 그렇지 않습니까? (아래 답변의 일부로이 답변에 답하십시오. 선택 / 제안한 사항을 밝히는 데 도움이됩니다 ...)
질문 : 수동 빌드에 빌드 번호를위한 장소가 있습니까? 모두가 CI 솔루션을 사용해야 할 정도로 문제가 있습니까?
질문 : 빌드 번호를 SCM에 체크인해야합니까? 목표가 확실하고 명확하게 특정 빌드를 식별하는 경우 충돌 / 재시작 등의 다양한 연속 또는 수동 빌드 시스템에 대처하는 방법 ...
질문 : 빌드 번호가 짧고 달콤합니다 (즉, 단조 증가하는 정수)하여 보관을 위해 파일 이름을 고수하기 쉽고 통신에서 참조하기 쉬우 며 ... 길고 사용자 이름이 길어야합니다. 날짜, 기계 이름 등?
질문 : 빌드 번호 할당이 대규모 자동 릴리스 프로세스에 어떻게 적용되는지 자세히 설명하십시오. 예, 메이븐 애호가들, 우리는 이것이 완료되었다는 것을 알고 있지만, 우리 모두가 아직 쿨 에이드를 마신 것은 아닙니다 ...
나는 적어도 cvs / ant / hudson 설정의 구체적인 예를 위해 이것을 완전한 대답으로 육체 화하고 싶습니다. 그래서 누군가이 질문에 기초하여 완전한 전략을 세울 수 있습니다. 이 특정 사례 (cvs 태그 지정 체계, 관련 CI 구성 항목 및 빌드 번호를 릴리스로 접어 놓는 릴리스 절차를 포함하여 프로그래밍 방식으로 설명)을 제공 할 수있는 사람이라면 누구나 "답변"으로 표시하겠습니다. 다른 특정 구성 (예 : svn / maven / cruise control)에 대해 질문 / 답변을하려면 여기에서 질문에 연결하겠습니다. --JA
[편집 10 10 월 9 일] 나는 그것이 합리적인 해결책이라고 생각하기 때문에 가장 인기있는 답변을 수락했지만 다른 답변들도 좋은 아이디어를 포함하고 있습니다. 누군가가 이들 중 일부를 marty-lamb 과 합성하기 위해 균열을 원한다면 다른 것을 받아들이는 것이 좋습니다. marty-lamb의 유일한 관심사는 직렬화 된 빌드 번호를 안정적으로 생성하지 않는다는 것입니다. 빌드 시스템의 현지 시계에 따라 모호하지 않은 빌드 번호를 제공하는데 이는 좋지 않습니다.
[7 월 10 일 편집]
이제 아래와 같은 클래스가 포함됩니다. 이를 통해 버전 번호를 최종 실행 파일로 컴파일 할 수 있습니다. 다양한 형태의 버전 정보가 로깅 데이터, 장기 보관 된 출력 제품에서 생성되며 출력 제품에 대한 (때로는 몇 년 후) 분석을 특정 빌드로 추적하는 데 사용됩니다.
public final class AppVersion
{
// SVN should fill this out with the latest tag when it's checked out.
private static final String APP_SVNURL_RAW =
"$HeadURL: svn+ssh://user@host/svnroot/app/trunk/src/AppVersion.java $";
private static final String APP_SVN_REVISION_RAW = "$Revision: 325 $";
private static final Pattern SVNBRANCH_PAT =
Pattern.compile("(branches|trunk|releases)\\/([\\w\\.\\-]+)\\/.*");
private static final String APP_SVNTAIL =
APP_SVNURL_RAW.replaceFirst(".*\\/svnroot\\/app\\/", "");
private static final String APP_BRANCHTAG;
private static final String APP_BRANCHTAG_NAME;
private static final String APP_SVNREVISION =
APP_SVN_REVISION_RAW.replaceAll("\\$Revision:\\s*","").replaceAll("\\s*\\$", "");
static {
Matcher m = SVNBRANCH_PAT.matcher(APP_SVNTAIL);
if (!m.matches()) {
APP_BRANCHTAG = "[Broken SVN Info]";
APP_BRANCHTAG_NAME = "[Broken SVN Info]";
} else {
APP_BRANCHTAG = m.group(1);
if (APP_BRANCHTAG.equals("trunk")) {
// this isn't necessary in this SO example, but it
// is since we don't call it trunk in the real case
APP_BRANCHTAG_NAME = "trunk";
} else {
APP_BRANCHTAG_NAME = m.group(2);
}
}
}
public static String tagOrBranchName()
{ return APP_BRANCHTAG_NAME; }
/** Answers a formatter String descriptor for the app version.
* @return version string */
public static String longStringVersion()
{ return "app "+tagOrBranchName()+" ("+
tagOrBranchName()+", svn revision="+svnRevision()+")"; }
public static String shortStringVersion()
{ return tagOrBranchName(); }
public static String svnVersion()
{ return APP_SVNURL_RAW; }
public static String svnRevision()
{ return APP_SVNREVISION; }
public static String svnBranchId()
{ return APP_BRANCHTAG + "/" + APP_BRANCHTAG_NAME; }
public static final String banner()
{
StringBuilder sb = new StringBuilder();
sb.append("\n----------------------------------------------------------------");
sb.append("\nApplication -- ");
sb.append(longStringVersion());
sb.append("\n----------------------------------------------------------------\n");
return sb.toString();
}
}
위키 토론이 될만한 의견이 있으면 남겨주세요.
내 프로젝트 중 일부의 경우 Subversion 개정 번호, 시간, 빌드를 실행 한 사용자 및 일부 시스템 정보를 캡처하여 응용 프로그램 jar에 포함되는 .properties 파일에 넣고 런타임에 해당 jar을 읽습니다.
개미 코드는 다음과 같습니다.
<!-- software revision number -->
<property name="version" value="1.23"/>
<target name="buildinfo">
<tstamp>
<format property="builtat" pattern="MM/dd/yyyy hh:mm aa" timezone="America/New_York"/>
</tstamp>
<exec executable="svnversion" outputproperty="svnversion"/>
<exec executable="whoami" outputproperty="whoami"/>
<exec executable="uname" outputproperty="buildsystem"><arg value="-a"/></exec>
<propertyfile file="path/to/project.properties"
comment="This file is automatically generated - DO NOT EDIT">
<entry key="buildtime" value="${builtat}"/>
<entry key="build" value="${svnversion}"/>
<entry key="builder" value="${whoami}"/>
<entry key="version" value="${version}"/>
<entry key="system" value="${buildsystem}"/>
</propertyfile>
</target>
추가하려는 정보를 포함하도록 이것을 확장하는 것은 간단합니다.
귀하 의 build.xml
...
<property name="version" value="1.0"/>
...
<target name="jar" depends="compile">
<buildnumber file="build.num"/>
<manifest file="MANIFEST.MF">
...
<attribute name="Main-Class" value="MyClass"/>
<attribute name="Implementation-Version" value="${version}.${build.number}"/>
...
</manifest>
</target>
...
자바 코드
String ver = MyClass.class.getPackage().getImplementationVersion();
- Build numbers should be associated with a continuous integration server like hudson. Use different jobs for different branches/teams/distributions.
- To keep the version number in the final build, I would recommend just using maven for build system. It will create a .properties file archived into the final .jar/.war/.whatever-ar on
META-INF/maven/<project group>/<project id>/pom.properties
. The .properties file will contain the version property. - Since I am recommending maven, I would urge you to check out the release plugin to prepare the release on source repository and keep the versions on sync.
Software:
- SVN
- Ant
- Hudson, for continuous integration
- svntask, an Ant task to find SVN revision: http://code.google.com/p/svntask/
Hudson has three builds/jobs: Continuous, Nightly and Release.
For a Continuous/Nightly build: Build number is the SVN revision, found using svntask.
For a Release build/job: Build number is the Release number, read by Ant, from a Properties file. The properties file can also be distributed with the release for displaying the build number at runtime.
The Ant build script puts the build number in the manifest file of jar/war files that are created during the build. Applies to all builds.
Post-build action for Release builds, done easily using a Hudson plug-in: tag SVN with the build number.
Benefits:
- For a dev version of a jar/war, the developer can find the SVN revision from the jar/war and look up the corresponding code in SVN
- For a release, the SVN revision is the one corresponding to the SVN tag that has the release number in it.
Hope this helps.
I'm using Hudson also, although a far more simpler scenario:
My Ant script has a target in it that looks like:
<target name="build-number">
<property environment="env" />
<echo append="false" file="${build.dir}/build-number.txt">Build: ${env.BUILD_TAG}, Id: ${env.BUILD_ID}, URL: ${env.HUDSON_URL}</echo>
</target>
Hudson sets these environment variables for me whenever my job runs.
In my case, this project is a webapp and I'm including this build-number.txt
file in the root folder of the webapp - I don't really care who sees it.
We don't tag source control when this is done because we already have our Hudson job set up to tag it with the build number/timestamp when the build is successful.
My solution only covers the incremental build numbers for development, we haven't gotten far enough in the project where we are covering release numbers yet.
You may also want to take a look at BuildNumber Maven plugin and Ant task in one jar found at http://code.google.com/p/codebistro/wiki/BuildNumber. I tried to make it simple and straightforward. It is a very small jar file that only depends on command line Subversion installed.
This is how i resolved this:
- the sources are copied to the build directory
- then the anttask "versioninfo" is applied
- compile the modified sources
Here is the java file storing the version info:
public class Settings {
public static final String VERSION = "$VERSION$";
public static final String DATE = "$DATE$";
}
And here is the anttask "versioninfo":
<!-- =================================
target: versioninfo
================================= -->
<target name="versioninfo"
depends="init"
description="gets version info from svn"
>
<!--
get svn info from the src folder
-->
<typedef resource="org/tigris/subversion/svnant/svnantlib.xml"
classpathref="ant.classpath"
/>
<svnSetting id="svn.setting"
javahl="false"
svnkit="true"
dateformatter="dd.MM.yyyy"
/>
<svn refid="svn.setting">
<info target="src" />
</svn>
<!--
if repository is a taged version use "v <tagname>"
else "rev <revisionnumber> (SVN)" as versionnumber
-->
<taskdef resource="net/sf/antcontrib/antcontrib.properties"
classpathref="ant.classpath"
/>
<propertyregex property="version"
input="${svn.info.url}"
regexp=".*/tags/(.*)/${ant.project.name}/src"
select="v \1"
defaultvalue="rev ${svn.info.lastRev} (SVN)"
override="true"
/>
<!--
replace date and version in the versionfile ()
-->
<replace file="build/${versionfile}">
<replacefilter token="$DATE$" value="${svn.info.lastDate}" />
<replacefilter token="$VERSION$" value="${version}" />
</replace>
</target>
Here is my 2 cents:
My build script creates a build number (with timestamp!) each time I build the app. This creates too many numbers but never too few. If I have a change in the code, the build number will change at least once.
I version the build number with every release (though not inbetween). When I update the project and I get a new build number (because someone else did a release), I overwrite my local version and start over. This can lead to a lower build number which is why I've included the timestamp.
When a release happens, the build number is committed as the last item in a single commit with the message "build 1547". After that, when it's an official release, the whole tree is tagged. This way, the build file always has all tags and there is a simple 1:1 mapping between tags and build numbers.
[EDIT] I deploy a version.html with my projects and then, I can use a scraper to simply collect an accurate map what is installed where. If you're using Tomcat or similar, put the build number and timestamp in the description
element of web.xml. Remember: Never memorize anything when you can have a computer do it for you.
We run our build via CruiseControl (insert your favourite build manager here), and perform the main build and tests.
We then increment the version number using Ant and BuildNumber and create a property file with this info plus the date of build and other metadata.
We have a class dedicated to reading this and providing it to GUIs/logs etc.
We then package all of this up and build a deployable tying together the build number and the corresponding build. All our servers dump this meta info on start up. We can go back through the CruiseControl logs and tie the build number to the date and checkins.
'Programing' 카테고리의 다른 글
Chrome에서 로컬 링크를 열 수 있습니까? (0) | 2020.06.29 |
---|---|
java.lang.Number가 Comparable을 구현하지 않는 이유는 무엇입니까? (0) | 2020.06.29 |
비트 버킷으로 푸시 한 후 Atlassian“로고” (0) | 2020.06.29 |
잠금 해제 된 뮤텍스를 잠그는 것이 얼마나 효율적입니까? (0) | 2020.06.29 |
HTML 5 doctype을 사용하지 않는 이유는 무엇입니까? (0) | 2020.06.29 |