Xây dựng và đánh số phiên bản cho các dự án Java (ant, cvs, hudson)


134

Các cách thực hành tốt nhất hiện nay để đánh số hệ thống và quản lý số phiên bản trong các dự án Java là gì? Đặc biệt:

  • Cách quản lý số lượng xây dựng một cách có hệ thống trong môi trường phát triển phân tán

  • Cách duy trì số phiên bản trong nguồn / khả dụng cho ứng dụng thời gian chạy

  • Cách tích hợp đúng với kho lưu trữ nguồn

  • Cách tự động quản lý số phiên bản so với thẻ kho

  • Cách tích hợp với cơ sở hạ tầng xây dựng liên tục

Có khá nhiều công cụ có sẵn và ant (hệ thống xây dựng chúng tôi đang sử dụng) có một nhiệm vụ sẽ duy trì số bản dựng, nhưng không rõ cách quản lý công cụ này với nhiều nhà phát triển đồng thời sử dụng CVS, svn hoặc tương tự .

[BIÊN TẬP]

Một số câu trả lời một phần hoặc cụ thể và hữu ích đã xuất hiện bên dưới, vì vậy tôi sẽ tóm tắt một vài trong số chúng. Đối với tôi có vẻ như không thực sự có một "thực tiễn tốt nhất" mạnh mẽ nào về vấn đề này, mà là một tập hợp các ý tưởng chồng chéo. Dưới đây, tìm tóm tắt của tôi và một số câu hỏi kết quả mà mọi người có thể cố gắng trả lời như tiếp theo. [Mới đối với stackoverflow ... vui lòng cung cấp nhận xét nếu tôi làm sai.]

  • Nếu bạn đang sử dụng SVN, phiên bản thanh toán cụ thể sẽ xuất hiện trong chuyến đi. Đánh số bản dựng có thể khai thác điều này để tạo số bản dựng duy nhất xác định thanh toán / sửa đổi cụ thể. [CVS, mà chúng tôi đang sử dụng vì lý do di sản, không cung cấp mức độ hiểu biết sâu sắc này ... can thiệp thủ công với các thẻ giúp bạn tham gia vào đó.]

  • Nếu bạn đang sử dụng maven làm hệ thống xây dựng của mình, có hỗ trợ sản xuất số phiên bản từ SCM, cũng như mô-đun phát hành để tự động tạo các bản phát hành. [Chúng tôi không thể sử dụng maven, vì nhiều lý do, nhưng điều này giúp những người có thể. [Cảm ơn marcelo-morales ]]

  • Nếu bạn đang sử dụng ant làm hệ thống xây dựng của mình, mô tả tác vụ sau đây có thể giúp tạo tệp Java .properations nắm bắt thông tin bản dựng, sau đó có thể được xếp vào bản dựng của bạn theo một số cách. [Chúng tôi đã mở rộng ý tưởng này để bao gồm thông tin có nguồn gốc từ hudson, cảm ơn marty-lamb ].

  • Ant và maven (và hudson và điều khiển hành trình) cung cấp các phương tiện dễ dàng để lấy số bản dựng thành tệp .properations hoặc vào tệp .txt / .html. Điều này có "an toàn" đủ để giữ cho nó khỏi bị giả mạo hay cố ý hay vô tình? Có tốt hơn để biên dịch nó thành một lớp "phiên bản" tại thời điểm xây dựng không?

  • Khẳng định: Xây dựng số nên được xác định / ban hành trong một hệ thống tích hợp liên tục như hudson . [Cảm ơn marcelo-morales ] Chúng tôi đã đưa ra gợi ý này, nhưng nó mở ra câu hỏi về kỹ thuật phát hành: Làm thế nào để phát hành xảy ra? Có nhiều bản dựng trong một bản phát hành không? Có một mối quan hệ có ý nghĩa giữa các nhà xây dựng từ các bản phát hành khác nhau?

  • Câu hỏi: Mục tiêu đằng sau số xây dựng là gì? Nó được sử dụng cho QA? Làm sao? Có phải nó được sử dụng chủ yếu bởi các nhà phát triển để phân tán giữa nhiều bản dựng trong quá trình phát triển hoặc nhiều hơn cho QA để xác định bản dựng nào mà người dùng cuối có? Nếu mục tiêu là khả năng tái tạo, theo lý thuyết thì đây là số phiên bản phát hành sẽ cung cấp - tại sao không? (vui lòng trả lời câu hỏi này như một phần câu trả lời của bạn bên dưới, nó sẽ giúp làm sáng tỏ những lựa chọn bạn đã thực hiện / đề xuất ...)

  • Câu hỏi: Có chỗ nào cho số bản dựng trong bản dựng thủ công không? Điều này có vấn đề đến nỗi MỌI NGƯỜI nên sử dụng giải pháp CI?

  • Câu hỏi: Có nên kiểm tra số xây dựng trong SCM không? Nếu mục tiêu đáng tin cậy và rõ ràng xác định một bản dựng cụ thể, làm thế nào để đối phó với nhiều hệ thống xây dựng liên tục hoặc thủ công có thể gặp sự cố / khởi động lại / v.v ...

  • Câu hỏi: Số xây dựng có nên ngắn và ngọt (nghĩa là số nguyên tăng đơn điệu) để dễ dàng gắn vào tên tệp để lưu trữ, dễ tham khảo trong giao tiếp, v.v ... hoặc nên dài và đầy đủ tên người dùng, dấu thời gian, tên máy, vv?

  • Câu hỏi: Vui lòng cung cấp chi tiết về cách gán số bản dựng phù hợp với quy trình phát hành tự động lớn hơn của bạn. Vâng, những người yêu thích maven, chúng tôi biết điều này đã được thực hiện và đã hoàn thành, nhưng không phải tất cả chúng ta đều đã uống kool-Aid khá ...

Tôi thực sự muốn đưa ra một câu trả lời hoàn chỉnh, ít nhất là cho ví dụ cụ thể về thiết lập cvs / ant / hudson của chúng tôi, để ai đó có thể xây dựng một chiến lược hoàn chỉnh dựa trên câu hỏi này. Tôi sẽ đánh dấu là "Câu trả lời" cho bất kỳ ai có thể đưa ra mô tả súp-to-nut cho trường hợp cụ thể này (bao gồm sơ đồ gắn thẻ cvs, các mục cấu hình CI có liên quan và quy trình phát hành có thể gấp số bản dựng vào bản phát hành sao cho theo lập trình có thể truy cập.) Nếu bạn muốn hỏi / trả lời cho một cấu hình cụ thể khác (giả sử, svn / maven / điều khiển hành trình) tôi sẽ liên kết đến câu hỏi từ đây. --JA

[EDIT 23 tháng 10 09] Tôi đã chấp nhận câu trả lời được bình chọn hàng đầu bởi vì tôi nghĩ đó là một giải pháp hợp lý, trong khi một số câu trả lời khác cũng bao gồm những ý tưởng hay. Nếu ai đó muốn có một cơ hội để tổng hợp một số trong số này với marty-lamb , tôi sẽ xem xét chấp nhận một cái khác. Mối quan tâm duy nhất tôi có với marty-lamb là nó không tạo ra số bản dựng nối tiếp đáng tin cậy - nó phụ thuộc vào đồng hồ địa phương tại hệ thống của nhà xây dựng để cung cấp số bản dựng rõ ràng, không tuyệt vời.

[Chỉnh sửa ngày 10 tháng 7]

Bây giờ chúng tôi bao gồm một lớp như dưới đây. Điều này cho phép các số phiên bản được biên dịch thành tệp thực thi cuối cùng. Các dạng khác nhau của thông tin phiên bản được phát ra trong dữ liệu ghi nhật ký, các sản phẩm đầu ra được lưu trữ dài hạn và được sử dụng để theo dõi phân tích (đôi khi nhiều năm sau) của chúng tôi về một sản phẩm cụ thể.

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();
  }
}

Để lại ý kiến ​​nếu điều này xứng đáng để trở thành một cuộc thảo luận wiki.


2
Đối với độc giả trong tương lai, xin lưu ý rằng số sửa đổi trong mã bạn đề xuất là của tệp, không phải là sửa đổi toàn cầu của kho lưu trữ. Để biết thêm thông tin, hãy xem: subversion.apache.org/faq.html#version-value-in-source
maayank

2
Tôi tự hỏi nếu có ai có cách tiếp cận đơn giản tương tự khi sử dụng gradlevà / hoặc git?
bnjmn

Câu trả lời:


63

Đối với một số dự án của tôi, tôi nắm bắt được số lần sửa đổi lật đổ, thời gian, người dùng đã chạy bản dựng và một số thông tin hệ thống, đưa chúng vào tệp .properations được đưa vào tệp ứng dụng và đọc tệp jar đó khi chạy.

Mã ant trông như thế này:

<!-- 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>

Thật đơn giản để mở rộng điều này để bao gồm bất kỳ thông tin nào bạn có thể muốn thêm.


12
Đối với phiên bản đa nền tảng, hãy sử dụng phiên bản này thay vì thuộc tính whoami ở trên: <entry key = "builder" value = "$ {user.name}" />
Ed Brannin

-1 để có một giải pháp phụ thuộc nền tảng. một cách hay để có tất cả các propoerties có sẵn trong một tệp là: `<property name =" antprops.file "location =" $ {build.temp.project.dir} / used_ant.properies "/> <echoproperies Destfile =" $ {antprops.file} "/> <! - sắp xếp tệp, CHỈ cho ant 1.7.0 và mới hơn !!! -> <concat> <union> <sort> <tokens> <file file =" $ {antprops .file} "/> <linetokenizer gộpelims =" true "/> </ tokens> </ sort> </ union> </ concat>`
raudi

Bạn có cam kết một lần nữa sau khi làm điều này? Hoặc điều này được thực hiện trước khi cam kết sửa đổi thực tế của bạn?
Charles Wood

6
Làm thế nào để làm điều này cho kho git?
TechCrunch

46

bạn 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>
...

Mã java của bạn

String ver = MyClass.class.getPackage().getImplementationVersion();

6
+1 để sử dụng một thuộc tính mà Java đã hỗ trợ với một phương thức như thế.
Ed Brannin

2
-1 vì có số bản dựng trong tệp build.xml chứ không phải trong tệp thuộc tính riêng biệt
raudi

6
  • Xây dựng số nên được liên kết với một máy chủ tích hợp liên tục như hudson . Sử dụng các công việc khác nhau cho các chi nhánh / nhóm / phân phối khác nhau.
  • Để giữ số phiên bản trong bản dựng cuối cùng, tôi khuyên bạn chỉ nên sử dụng maven cho hệ thống bản dựng. Nó sẽ tạo một tệp .properies được lưu trữ vào .jar / .war / .whthing-ar trên META-INF/maven/<project group>/<project id>/pom.properties. Tệp .properIES sẽ chứa thuộc tính phiên bản.
  • Vì tôi khuyên bạn nên dùng maven, tôi khuyên bạn nên kiểm tra plugin phát hành để chuẩn bị phát hành trên kho lưu trữ nguồn và giữ cho các phiên bản được đồng bộ hóa.

6

Phần mềm:

  • SVN
  • Con kiến
  • Hudson, để tích hợp liên tục
  • svntask, một tác vụ Ant để tìm bản sửa đổi SVN: http://code.google.com.vn/p/svntask/

Hudson có ba bản dựng / công việc: Liên tục, Hàng đêm và Phát hành.

Đối với bản dựng liên tục / hàng đêm: Số bản dựng là bản sửa đổi SVN, được tìm thấy bằng cách sử dụng svntask.

Đối với bản dựng / công việc Phát hành: Số bản dựng là số Bản phát hành, được đọc bởi Ant, từ tệp Thuộc tính. Tệp thuộc tính cũng có thể được phân phối cùng với bản phát hành để hiển thị số bản dựng khi chạy.

Tập lệnh xây dựng Ant đặt số bản dựng trong tệp kê khai của tệp jar / war được tạo trong quá trình xây dựng. Áp dụng cho tất cả các bản dựng.

Hành động xây dựng sau cho các bản dựng Phát hành, được thực hiện dễ dàng bằng cách sử dụng trình cắm thêm Hudson: thẻ SVN với số bản dựng.

Những lợi ích:

  • Đối với phiên bản dev của jar / war, nhà phát triển có thể tìm thấy bản sửa đổi SVN từ jar / war và tìm mã tương ứng trong SVN
  • Đối với bản phát hành, bản sửa đổi SVN là bản tương ứng với thẻ SVN có số bản phát hành trong đó.

Hi vọng điêu nay co ich.


Bạn có bản phát hành bản dựng / công việc kiểm tra tệp thuộc tính số bản dựng / bản phát hành trở lại SVN không?
matt b

Đối với các bản dựng liên tục, số bản dựng là số sửa đổi SVN. Vì vậy, không có gì để đăng nhập. Đối với các bản dựng phát hành, đó là tệp đăng ký được sử dụng để lấy số bản dựng. Thông thường, kỹ sư phát hành hoặc bất kỳ ai khác muốn cập nhật tốt tệp này trước khi phát hành.
Raleigh

4

Tôi cũng đang sử dụng Hudson, mặc dù kịch bản đơn giản hơn nhiều:

Kịch bản Ant của tôi có một mục tiêu trong đó trông giống như:

<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 đặt các biến môi trường này cho tôi bất cứ khi nào công việc của tôi chạy.

Trong trường hợp của tôi, dự án này là một ứng dụng web và tôi bao gồm build-number.txttệp này trong thư mục gốc của ứng dụng web - tôi không thực sự quan tâm ai nhìn thấy nó.

Chúng tôi không gắn thẻ kiểm soát nguồn khi việc này được thực hiện vì chúng tôi đã thiết lập công việc Hudson của mình để gắn thẻ với số xây dựng / dấu thời gian khi quá trình xây dựng thành công.

Giải pháp của tôi chỉ bao gồm các số lượng xây dựng gia tăng để phát triển, chúng tôi chưa đủ xa trong dự án nơi chúng tôi đang bao gồm các số phát hành.


ngoài ra (ít nhất là trong các phiên bản jenkins hiện tại), bạn có thể kiểm tra các thuộc tính: `env.SVN_URL_1 env.SVN_REVISION_1 env.SVN_URL_2 env.SVN_REVISION_2, v.v.
raudi

3

Bạn cũng có thể muốn xem plugin BuildNumber Maven và tác vụ Ant trong một jar được tìm thấy tại http://code.google.com.vn/p/codebistro/wiki/BuildNumber . Tôi đã cố gắng làm cho nó đơn giản và đơn giản. Nó là một tệp jar rất nhỏ chỉ phụ thuộc vào dòng lệnh Subversion được cài đặt.


3

Đây là cách tôi giải quyết điều này:

  • các nguồn được sao chép vào thư mục xây dựng
  • sau đó anttask "versioninfo" được áp dụng
  • biên dịch các nguồn sửa đổi

Đây là tập tin java lưu trữ thông tin phiên bản:

public class Settings {

    public static final String VERSION = "$VERSION$";
    public static final String DATE = "$DATE$";

}

Và đây là 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>

2

Đây là 2 xu của tôi:

  • Tập lệnh xây dựng của tôi tạo số xây dựng (có dấu thời gian!) Mỗi ​​lần tôi xây dựng ứng dụng. Điều này tạo ra quá nhiều số nhưng không bao giờ quá ít. Nếu tôi có thay đổi trong mã, số bản dựng sẽ thay đổi ít nhất một lần.

  • Tôi phiên bản số bản dựng với mỗi bản phát hành (mặc dù không nằm giữa). Khi tôi cập nhật dự án và tôi nhận được số bản dựng mới (vì người khác đã phát hành), tôi đã ghi đè lên phiên bản địa phương của mình và bắt đầu lại. Điều này có thể dẫn đến số bản dựng thấp hơn, đó là lý do tại sao tôi đã bao gồm dấu thời gian.

  • Khi một bản phát hành xảy ra, số bản dựng được cam kết là mục cuối cùng trong một cam kết duy nhất với thông báo "bản dựng 1547". Sau đó, khi nó là một bản phát hành chính thức, toàn bộ cây được gắn thẻ. Bằng cách này, tệp xây dựng luôn có tất cả các thẻ và có ánh xạ 1: 1 đơn giản giữa các thẻ và số bản dựng.

[EDIT] Tôi triển khai một phiên bản.html với các dự án của mình và sau đó, tôi có thể sử dụng một dụng cụ cạo để thu thập bản đồ chính xác những gì được cài đặt ở đâu. Nếu bạn đang sử dụng Tomcat hoặc tương tự, hãy đặt số bản dựng và dấu thời gian trong descriptionphần tử của tệp web.xml . Hãy nhớ rằng: Không bao giờ ghi nhớ bất cứ điều gì khi bạn có thể có một máy tính làm điều đó cho bạn.


Vâng ... đó là những gì chúng ta có quá. Và đó là một nỗi đau để sử dụng, khi bạn phải nhớ bản dựng dài vô cùng nào đã được triển khai ở đây hay ở đó. Quá nhiều con số cũng là rắc rối ...
Varkhan

@Varkhan: Tại sao? Chỉ cần đặt phiên bản ở một nơi nào đó mà trình quét HTML có thể thu thập nó cho bạn ...
Aaron Digulla

1

Chúng tôi chạy bản dựng của mình thông qua CruiseControl (chèn trình quản lý bản dựng yêu thích của bạn vào đây) và thực hiện bản thử nghiệm và bản dựng chính.

Sau đó, chúng tôi tăng số phiên bản bằng Ant và BuildNumber và tạo tệp thuộc tính với thông tin này cộng với ngày xây dựng và siêu dữ liệu khác.

Chúng tôi có một lớp dành riêng để đọc nó và cung cấp nó cho GUI / nhật ký, v.v.

Sau đó, chúng tôi đóng gói tất cả những thứ này lên và xây dựng một sự ràng buộc có thể triển khai cùng với số bản dựng và bản dựng tương ứng. Tất cả các máy chủ của chúng tôi kết xuất thông tin meta này khi khởi động. Chúng ta có thể quay lại nhật ký CruiseControl và gắn số bản dựng vào ngày và đăng ký.


Mẹo: Nếu bạn đang sử dụng BuildNumber của CC và Ant, bạn có thể sử dụng PropertyFileLabelIncrementer để giữ nhãn CC đồng bộ với số bản dựng.
Jeffrey Fredrick
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.