Java Projeleri için Derleme ve Sürüm Numaralandırma (ant, cvs, hudson)


134

Java projelerinde sistematik yapı numaralandırma ve sürüm numarası yönetimi için mevcut en iyi uygulamalar nelerdir? özellikle:

  • Dağıtılmış geliştirme ortamında yapı sayılarını sistematik olarak yönetme

  • Sürüm numaralarını kaynakta tutma / çalışma zamanı uygulaması için kullanılabilir

  • Kaynak havuzla düzgün bir şekilde nasıl entegre edilir

  • Sürüm numaralarının depo etiketlerine karşı daha otomatik olarak yönetilmesi

  • Sürekli yapı altyapısı ile entegrasyon

Oldukça fazla sayıda araç var ve karınca (kullandığımız inşa sistemi) bir inşa numarasını koruyacak bir göreve sahip, ancak CVS, svn veya benzeri kullanan birden çok, eşzamanlı geliştirici ile bunun nasıl yönetileceği açık değil .

[DÜZENLE]

Aşağıda birkaç iyi ve yararlı kısmi veya özel cevap ortaya çıktı, bu yüzden birkaçını özetleyeceğim. Bana öyle geliyor ki, bu konuda gerçekten güçlü bir “en iyi uygulama” değil, örtüşen fikirlerin bir koleksiyonu. Aşağıda, özetlerimi ve insanların takip olarak cevaplamaya çalışabilecekleri bazı soruları bulabilirsiniz. [Stackoverflow'da yeni… Bunu yanlış yapıyorsam lütfen yorum sağlayın.]

  • SVN kullanıyorsanız, sürüş için belirli bir kasanın sürümlendirilmesi gelir. Yapı numaralandırması, belirli bir ödeme / revizyonu tanımlayan benzersiz bir yapı numarası oluşturmak için bundan yararlanabilir. [Eski nedenlerden dolayı kullandığımız CVS, bu düzeyde bir içgörü sağlamaz ... etiketlere manuel müdahale sizi oraya kısmen götürür.]

  • Oluşturma sisteminiz olarak maven kullanıyorsanız, SCM'den bir sürüm numarası üretmenin yanı sıra otomatik olarak sürümleri üretmek için bir serbest bırakma modülü de vardır. [Biz maven'i çeşitli nedenlerle kullanamayız, ancak bu yapabileceklerine yardımcı olur. [ Marcelo-morales sayesinde ]]

  • Derleme sisteminiz olarak karınca kullanıyorsanız , aşağıdaki görev açıklaması derleme bilgilerini yakalayan bir Java .properties dosyası oluşturmanıza yardımcı olabilir, bu da derlemenize birkaç şekilde katlanabilir. [Bu fikri hudson kaynaklı bilgileri içerecek şekilde genişlettik, teşekkürler şehit-kuzu ].

  • Ant ve maven (ve hudson ve cruise control) yapı numaralarını bir .properties dosyasına veya .txt / .html dosyasına almak için kolay araçlar sağlar. Bu, kasıtlı olarak veya yanlışlıkla değiştirilmesini önleyecek kadar "güvenli" midir? Oluşturma zamanında bir "sürüm oluşturma" sınıfında derlemek daha mı iyi?

  • Onaylama: Yapı numaralandırması, hudson gibi sürekli bir entegrasyon sisteminde tanımlanmalı / yürürlüğe konmalıdır . [ Marcelo-morales sayesinde ] Bu öneriyi aldık, ancak sürüm mühendisliği sorusunu açıyor: Bir sürüm nasıl oluyor? Bir sürümde birden fazla yapı numarası var mı? Farklı sürümlerden yapı oluşturucuları arasında anlamlı bir ilişki var mı?

  • Soru: Yapı numarasının ardındaki amaç nedir? KG için kullanılıyor mu? Nasıl? Öncelikle geliştiriciler tarafından geliştirme sırasında birden çok yapı arasında ayrım yapmak için mi yoksa bir QA için son kullanıcının hangi yapının varlığını belirlemek için daha fazla mı kullanılır? Hedef tekrarlanabilirlik ise, teorik olarak bir sürüm sürüm numarası budur - neden olmasın? (lütfen aşağıdaki cevaplarınızın bir parçası olarak cevaplayın, yaptığınız / önerdiğiniz seçimlerin aydınlatılmasına yardımcı olacaktır ...)

  • Soru: Manuel yapılarda yapı numaraları için bir yer var mı? Bu HERKES'in bir CI çözümü kullanması gerektiği kadar sorunlu mu?

  • Soru: Yapı numaraları SCM'de kontrol edilmeli mi? Hedef, belirli bir yapıyı güvenilir ve açık bir şekilde tanımlamaksa, çökme / yeniden başlatma / vb.Gibi çeşitli sürekli veya manuel oluşturma sistemleriyle nasıl başa çıkılacağı ...

  • Soru: Bir yapı numarası kısa ve tatlı olmalı (yani, monoton olarak artan tamsayı), böylece arşiv için dosya adlarına kolayca yapışabilir, iletişimde referans verilebilir, vb ... veya uzun ve kullanıcı adlarıyla dolu olmalıdır tarih damgaları, makine adları, vb?

  • Soru: Lütfen yapı numaralarının atanmasının daha büyük otomatik yayınlama işleminize nasıl uyduğuyla ilgili ayrıntıları sağlayın. Evet, maven severler, bunun yapıldığını ve yapıldığını biliyoruz, ancak hepimiz kool yardımını henüz sarmadık ...

Bunu en azından cvs / ant / hudson kurulumumuzun somut örneği için tam bir cevaba dönüştürmek istiyorum, böylece birisi bu soruya dayanarak tam bir strateji oluşturabilir. Bu özel durum için çorba-fındık açıklaması verebilen herkes "Cevap" olarak işaretleyeceğim (cvs etiketleme şeması, ilgili CI yapılandırma öğeleri ve sürüm numarasını sürüm olarak programa katlanacak şekilde sürüme katlayan sürüm yordamı Başka bir belirli yapılandırma istemek / yanıtlamak istiyorsanız (örneğin, svn / maven / cruise control) soruyu buradan bağlayacağım. --JA

[EDIT 23 Eki 09] En çok oy alan yanıtı kabul ediyorum, çünkü bunun makul bir çözüm olduğunu düşünüyorum, diğer cevapların bazıları da iyi fikirler içeriyor. Birisi bunlardan bazılarını şehit kuzu ile sentezlemek için bir çatlak almak istiyorsa , farklı bir tane kabul etmeyi düşüneceğim. Şehit kuzu ile ilgili tek endişe, güvenilir bir şekilde seri hale getirilmiş bir yapı numarası üretmemesidir - büyük olmayan açık yapı numaraları sağlamak için inşaatçının sistemindeki yerel bir saate bağlıdır.

[10 Temmuz'u Düzenle]

Şimdi aşağıdaki gibi bir sınıf ekliyoruz. Bu, sürüm numaralarının son yürütülebilir dosyada derlenmesini sağlar. Günlüğe kaydetme verilerinde, uzun vadeli arşivlenmiş çıktı ürünlerinde farklı sürüm bilgisi formları yayınlanır ve çıktı ürünlerinin (bazen yıllar sonra) analizini belirli bir yapıya izlemek için kullanılır.

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

Bu bir wiki tartışması olmayı hak ediyorsa yorum bırakın.


2
Gelecekteki okuyucular için, önerdiğiniz koddaki revizyon numarasının deponun global revizyonu değil, dosyaya ait olduğunu lütfen unutmayın. Daha fazla bilgi için, bkz. Subversion.apache.org/faq.html#version-value-in-source
maayank

2
gradleVe / veya kullanırken benzer yaklaşımlar olup olmadığını merak ediyorum git?
bnjmn

Yanıtlar:


63

Projelerimin birçoğu için alt sürüm revizyon numarasını, süreyi, derlemeyi çalıştıran kullanıcıyı ve bazı sistem bilgilerini yakalarım, bunları uygulama kavanozuna eklenen bir .properties dosyasına dolduruyorum ve çalışma zamanında bu kavanozu okuyorum.

Karınca kodu şöyle görünür:

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

Eklemek isteyebileceğiniz bilgileri içerecek şekilde genişletmek kolaydır.


12
Platformlar arası bir sürüm için yukarıdaki whoami özelliği yerine bunu kullanın: <entry key = "builder" value = "$ {
user.name

Platforma bağımlı bir çözüme sahip olduğu için -1. tüm karıncalanıcı özelliklerin bir dosyaya yerleştirilmesinin güzel bir yolu: `` <property name = "antprops.file" location = "$ {build.temp.project.dir} /used_ant.properties" /> <echoproperties destfile = "$ {antprops.file} "/> <! - dosyayı SADECE karınca 1.7.0 ve üstü için sıralayın !!! -> <concat> <union> <sort> <tokens> <dosya dosyası =" $ {antprops .file} "/> <linetokenizer dahilelims =" true "/> </tokens> </sort> </union> </concat>`
raudi

Bunu yaptıktan sonra tekrar taahhüt eder misin? Yoksa bu, gerçek düzeltmelerinizi gerçekleştirmeden önce mi yapılır?
Charles Wood

6
Git deposu için bunu nasıl yapabilirim?
TechCrunch

46

Build.xml dosyanız

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

Java kodunuz

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

6
Java'nın zaten böyle bir yöntemle desteklediği bir özelliği kullanmak için +1.
Ed Brannin


6
  • Derleme numaraları, hudson gibi sürekli bir entegrasyon sunucusuyla ilişkilendirilmelidir . Farklı şubeler / ekipler / dağıtımlar için farklı işler kullanın.
  • Sürüm numarasını son derlemede tutmak için derleme sistemi için sadece maven kullanmanızı öneririm . Son .jar / .war / .whatever-ar dosyasında arşivlenmiş bir .properties dosyası oluşturur META-INF/maven/<project group>/<project id>/pom.properties. .Properties dosyası version özelliğini içerecektir.
  • Maven'i tavsiye ettiğim için, kaynak deposundaki sürümü hazırlamak ve sürümleri senkronize tutmak için sürüm eklentisini kontrol etmenizi öneririm .

6

Yazılım:

  • SVN
  • Karınca
  • Hudson, sürekli entegrasyon için
  • svntask, SVN revizyonunu bulmak için bir Ant görevi: http://code.google.com/p/svntask/

Hudson'ın üç yapısı / işi vardır: Sürekli, Gece ve Serbest Bırakma.

Sürekli / Gecelik derleme için: Derleme numarası, svntask kullanılarak bulunan SVN düzeltmesidir.

Bir Sürüm derlemesi / işi için: Derleme numarası, Özellikler dosyasından Ant tarafından okunan Sürüm numarasıdır. Özellikler dosyası, çalışma zamanında yapı numarasını görüntülemek için sürümle birlikte dağıtılabilir.

Ant derleme betiği, derleme numarasını derleme sırasında oluşturulan jar / war dosyalarının bildirim dosyasına koyar. Tüm derlemeler için geçerlidir.

Yayın eklentileri için derleme sonrası eylem, Hudson eklentisi kullanılarak kolayca yapılır: derleme numarasıyla SVN etiketi.

Yararları:

  • Bir jar / savaşın geliştirici sürümü için, geliştirici jar / savaştan SVN revizyonunu bulabilir ve SVN'de ilgili kodu arayabilir
  • Bir sürüm için SVN düzeltmesi, içinde sürüm numarası bulunan SVN etiketine karşılık gelen düzeltmedir.

Bu yardımcı olur umarım.


Sürüm derleme / işi derleme / sürüm numarası özellikleri dosyasını tekrar SVN'ye kontrol ettiniz mi?
matt b

Sürekli derlemeler için derleme numarası SVN revizyon numarasıdır. Yani, kontrol edilecek bir şey yok. Sürüm derlemeleri için derleme numarasını almak için kullanılan teslim edilen dosyadır. Genellikle, sürüm mühendisi veya başka biri bu dosyayı sürümden önce güncellemek ister.
Raleigh

4

Hudson'ı da çok daha basit bir senaryo olmasına rağmen kullanıyorum:

Karınca komut dosyamın içinde şöyle bir hedef var:

<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, işim her çalıştığında bu ortam değişkenlerini benim için belirliyor.

Benim durumumda, bu proje bir webapp ve bu build-number.txtdosyayı webapp'ın kök klasörüne ekliyorum - gerçekten kimin gördüğü umrumda değil.

Bu yapıldığında kaynak kontrolünü etiketlemiyoruz, çünkü yapı başarılı olduğunda Hudson işimizi derleme numarası / zaman damgasıyla etiketlemek için ayarlanmış durumdayız.

Çözümüm yalnızca geliştirme için artan yapı numaralarını kapsamakta, henüz sürüm numaralarını kapsadığımız projede yeterince fazla bir şey elde edemedik.


ayrıca (en azından mevcut jenkins sürümlerinde) şu özelliklere göz atabilirsiniz: `env.SVN_URL_1 env.SVN_REVISION_1 env.SVN_URL_2 env.SVN_REVISION_2 vb.
raudi


3

Ben bu nasıl çözüldü:

  • kaynaklar derleme dizinine kopyalanır
  • sonra anttask "versioninfo" uygulanır
  • değiştirilmiş kaynakları derleyin

Sürüm bilgilerini saklayan java dosyası:

public class Settings {

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

}

Ve burada 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

İşte benim 2 sent:

  • Uygulama betiğim, uygulamayı her oluşturduğumda (zaman damgası ile!) Bir yapı numarası oluşturur. Bu çok fazla sayı oluşturur, ancak asla çok az sayılamaz. Kodda bir değişiklik varsa, yapı numarası en az bir kez değişecektir.

  • Her sürümde yapı numarasını sürüm olarak yazıyorum (aralarında olmasa da). Projeyi güncellediğimde ve yeni bir yapı numarası aldığımda (bir başkası sürüm yayınladığından), yerel sürümümün üzerine yazıyorum ve baştan başladım. Bu, daha düşük bir yapı numarasına yol açabilir, bu yüzden zaman damgasını ekledim.

  • Bir sürüm gerçekleştiğinde, yapı numarası "yapı 1547" iletisiyle tek bir taahhütte son öğe olarak işlenir. Bundan sonra, resmi bir sürüm olduğunda, tüm ağaç etiketlenir. Bu şekilde, derleme dosyası her zaman tüm etiketlere sahiptir ve etiketler ile derleme numaraları arasında basit bir 1: 1 eşleme vardır.

[EDIT] Projelerimle bir version.html dosyası dağıtıyorum ve sonra nereye yüklendiğini doğru bir harita toplamak için bir kazıyıcı kullanabilirim. Tomcat veya benzeri bir sürüm kullanıyorsanız, derleme numarasını ve zaman damgasını web.xmldescription öğesine yerleştirin . Unutmayın: Bir bilgisayarın sizin için yapmasını sağlayabildiğinizde hiçbir şeyi ezberlemeyin.


Evet ... bizde de var. Ve burada veya orada hangi inanılmaz uzun yapının dağıtıldığını hatırlamanız gerektiğinde, kullanmak bir acıdır. Çok fazla sayı da sorun ...
Varkhan

@Varkhan: Neden? Sürümü bir HTML kazıyıcısının sizin için toplayabileceği bir yere koyun ...
Aaron Digulla

1

Derlememizi CruiseControl (en sevdiğiniz derleme yöneticinizi buraya ekleyin) üzerinden çalıştırıyor, ana derlemeyi ve testleri gerçekleştiriyoruz.

Sonra Ant ve BuildNumber kullanarak sürüm numarasını artırır ve bu bilgi artı derleme tarihi ve diğer meta veriler ile bir özellik dosyası oluştururuz.

Bunu okumaya ve GUI'lere / günlüklere vb. Sunmaya adanmış bir sınıfımız var.

Daha sonra tüm bunları paketliyoruz ve yapı numarasını ve ilgili yapıyı birleştirerek konuşlandırılabilir bir bağlantı kuruyoruz. Tüm sunucularımız başlangıçta bu meta bilgileri atar. CruiseControl günlüklerine geri dönebilir ve yapı numarasını tarihe ve checkin'lere bağlayabiliriz.


İpucu: CC ve Ant'in BuildNumber'ını kullanıyorsanız, CC etiketlerini derleme numarasıyla senkronize tutmak için PropertyFileLabelIncrementer'ı kullanabilirsiniz.
Jeffrey Fredrick
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.