Makineye Özgü Yapılandırma Dosyalarını İşleme


82

Geliştirdiğimde yaygın bir senaryo, kod tabanının makineye özel ayarlar gerektiren birkaç yapılandırma dosyasına sahip olmasıdır. Bu dosyalar Git'te kontrol edilecek ve diğer geliştiriciler her zaman yanlışlıkla onları tekrar teslim edecek ve başka birinin yapılandırmasını bozacak.

Bunun basit bir çözümü, onları Git'e teslim etmemek veya hatta onlar için ek olarak bir .gitignore girişi eklemektir. Bununla birlikte, dosyada geliştiricinin ihtiyaçlarına göre değiştirebileceği bazı mantıklı varsayılanlara sahip olmanın çok daha zarif olduğunu görüyorum.

Git'in bu tür dosyalarla güzelce oynamasını sağlamanın zarif bir yolu var mı? Makineye özgü bir yapılandırma dosyasını değiştirebilmek ve ardından bu dosyayı kontrol etmeden "git commit -a" komutunu çalıştırabilmek istiyorum.


1
Bu, tasarımınızda ve meslektaşınızın beyninde bir probleminiz varmış gibi görünüyor. Onlara bir kaynak kontrol sistemine neyi dahil ettiklerini bildiklerinden emin olmalarını söyleyin, aksi takdirde istemedikleri şeyleri kontrol ederler. Ayrıca: Neden dosyayı her sistem için bir dosya olarak bölmeyesiniz?
Pod

11
Bunun oldukça yaygın bir senaryo olduğuna eminim. Makineye özel yapılandırmayı nasıl takip ediyorsunuz? Dosyayı her sistem için bölmek oldukça dağınık görünüyor ve dağıtılmış sürüm kontrolüne sahip olma amacını ortadan
kaldırıyor

1
Hepinizin ittiği paylaşılan depoda bir ön güncelleme kancası kullanarak en azından son işlemlerin getirilmesini önleyebilirsiniz. Belirli geliştiriciler tarafından yapılan yapılandırma dosyasını değiştiren commit'leri arayabilir veya mesajda özel anahtar kelimeden bahsetmeyen o dosyaya dokunan commit'leri arayabilir.
Phil Miller

2
+ 1, bu ise ortak bir sorun. @Pod: Depoda "Joe.conf" olması pratik değildir, ancak yine de bazı şeyleri güncelleyebilmek istiyorsunuz ... bazen koddaki değişiklikler nedeniyle yapılandırmalarda değişiklik olması gerekir.
Thanatos

Yanıtlar:


59

Programınızın ayarları için bir çift yapılandırma dosyası okumasını sağlayın. Öncelikle, config.defaultsarşive dahil edilecek bir dosyayı okumalıdır . Ardından, config.locallistelenmesi gereken bir dosyayı okumalıdır.gitignore

Bu düzenleme ile, yeni ayarlar varsayılanlar dosyasında görünür ve güncellenir güncellenmez etkili olur. Yalnızca geçersiz kılınırlarsa belirli sistemlere göre değişiklik gösterirler.

Bunun bir varyasyonu olarak, configsürüm kontrolünde include config.localgönderdiğiniz genel bir dosyaya sahip olabilirsiniz ve makineye özgü değerleri getirmek gibi bir şey yapmasını sağlayabilirsiniz. Bu, kodunuzda daha genel bir mekanizma (politikaya karşı) sunar ve sonuç olarak daha karmaşık yapılandırmalara olanak tanır (uygulamanız için isteniyorsa). Birçok büyük ölçekli açık kaynaklı yazılımda görülen, bunun popüler uzantısı, include conf.dbir dizindeki tüm dosyalardan yapılandırmayı okuyan eklentidir .

Ayrıca benzer bir soruya verdiğim cevaba bakın .


Buna cevabı vereceğim. Bu yöntem, uygulama tarafında ekstra mantık gerektirmesinin tek dezavantajı ile istenen etkiyi elde eder.
ghempton

17

Deneyebilirsin git update-index --skip-worktree filename. Bu git'e dosya adında yerel değişiklikler yokmuş gibi davranmasını söyleyecektir, bu yüzden git commit -aonu görmezden gelecektir. Ayrıca direnme avantajına da sahiptir git reset --hard, böylece yerel değişikliklerinizi yanlışlıkla kaybetmezsiniz. Ayrıca, dosya yukarı yönde değiştirilirse, otomatik birleştirmeler sorunsuz bir şekilde başarısız olur (çalışma dizini kopyası dizin kopyasıyla eşleşmediği sürece, bu durumda otomatik olarak güncellenir). Olumsuz tarafı, komutun ilgili tüm makinelerde çalıştırılması gerektiğidir ve bunu otomatik olarak yapmak zordur. git update-index --assume-unchangedBu fikrin oldukça farklı bir versiyonu için de bakınız . Her ikisiyle ilgili ayrıntılar ile bulunabilir git help update-index.


Bu komutlar hakkında daha fazla bilgiyi 'değişmediğini varsaymak' ve 'ağacı atla' arasındaki fark sorusunda bulabilirsiniz . En iyi cevaptan , --skip-worktreebu durumda istediğiniz gibi görünüyor .
Senseful

10

Başka bir yaklaşım, başka bir özel şubedeki ortak yapılandırma dosyalarında yerel değişiklikleri sürdürmektir. Bunu birkaç yerel değişiklik gerektiren bazı projeler için yapıyorum. Bu teknik tüm durumlar için geçerli olmayabilir, ancak bazı durumlarda benim için işe yarıyor.

İlk önce ana dalı temel alan yeni bir dal oluşturuyorum (bu özel durumda git-svn kullanıyorum, bu yüzden ustadan taahhüt etmem gerekiyor ama bu burada çok önemli değil):

git checkout -b work master

Şimdi yapılandırma dosyalarını gerektiği gibi değiştirin ve kesin. Genellikle commit mesajına "NOCOMMIT" veya "PRIVATE" gibi ayırt edici bir şey koyarım (bu daha sonra faydalı olacaktır). Bu noktada, kendi yapılandırma dosyanızı kullanarak özel şubenizde çalışabilirsiniz.

Çalışmanızı tekrar yukarı akışa itmek istediğinizde, şubenizden workustaya her değişikliği özenle seçin . Bunu yapmanıza yardımcı olacak bir senaryom var ve şuna benzer:

#!/bin/sh

BRANCH=`git branch | grep ^\\* | cut -d' ' -f2`
if [ $BRANCH != "master" ]; then
  echo "$0: Current branch is not master"
  exit 1
fi

git log --pretty=oneline work...master | grep -v NOCOMMIT: | cut -d' ' -f1 | tac | xargs -l git cherry-pick

Bu, ilk olarak masterşubede olduğumdan emin olmak için kontrol eder (akıl sağlığı kontrolü). Daha sonra, her bir workkaydı listeler , NOCOMMIT anahtar sözcüğünden bahsedenleri filtreler, sırayı tersine çevirir ve son olarak her bir kaydı (şimdi en eskisinden ilk) içine çeker master.

Son olarak, ana yukarı workakıştaki değişiklikleri zorladıktan sonra, geri dönüp yeniden tabanlarım:

git checkout work
git rebase master

Git, workdaldaki işlemlerin her birini yeniden uygulayarak master, kiraz toplama yoluyla zaten uygulanmış olanları etkili bir şekilde atlayacaktır . Size bırakılması gereken şey sadece NOCOMMIT yerel taahhütleridir.

Bu teknik, itme sürecini biraz daha zaman alıcı hale getiriyor, ancak benim için bir sorunu çözdü, bu yüzden paylaşacağımı düşündüm.


2
Unutulan soru sormayan kişiden bunu yapmasını istediğinin farkında mısın? git commit -aDünyada hiç umursamadan koşan kişi mi?
Phil Miller

Aynı stratejiyi git rebase --ontogit fetch
izleyerek

8

Bir olasılık, gerçek dosyaları .gitignore dosyanızda bulundurmak, ancak varsayılan yapılandırmaları farklı bir uzantı ile kontrol etmektir. Bir Rails uygulaması için tipik bir örnek, config / database.yml dosyasıdır. Config / database.yml.sample dosyasını kontrol ederdik ve her geliştirici zaten .gitignored olan kendi config / database.yml dosyasını oluşturur.


Evet, bu aşamalı bir iyileştirmedir, ancak kontrol edilen sürüm kasıtlı olarak değiştirilirse, geliştirici yapılandırma dosyalarına yansıtılmadığından hala optimal değildir. Bunun ne zaman yararlı olacağına bir örnek, yeni bir mülk eklendiğinde vb.
ghempton

Bu, bir özellik ayarlanmadığında şikayet eden iyi işlem notları ve açıklayıcı hata mesajları içeren bir adres olabilir. Ekibinize değişiklik hakkında iletişim kuran bir e-posta da yardımcı olur.
Brian Kelly

Bu çözüm hakkında daha fazla bilgi ve harika bir örnek için bu yanıta bakın .
Senseful

1

Farklı bir uzantıya sahip varsayılan bir yapılandırmayı kontrol edin (örneğin .default), varsayılanı doğru konuma simgelemek için bir sembolik bağ kullanın, doğru konumu .gitignore'a ekleyin ve yapılandırmayla ilgili diğer her şeyi .gitignore'a ekleyin (bu nedenle yalnızca kontrol edilen şey config.default'dur).

Ek olarak, uygulama genelinde sembolik bağları kuran bir hızlı kurulum betiği yazın.

Önceki bir şirkette benzer bir yaklaşım kullandık. Kurulum betiği, hangi ortamda çalıştığınızı (korumalı alan, geliştirme, QA, üretim) otomatik olarak algıladı ve otomatik olarak doğru olanı yapacaktır. Bir config.sandbox dosyanız varsa ve sanal alandan çalıştırıyorsanız, onu bağlar (aksi takdirde yalnızca .defaults dosyasını bağlar). Yaygın prosedür, .default'ları kopyalamak ve ayarları gerektiği gibi değiştirmekti.

Kurulum betiğini yazmak, hayal edebileceğinizden daha kolaydır ve size çok fazla esneklik sağlar.


1

En iyi yanıta katılıyorum ama aynı zamanda bir şeyler eklemek istiyorum. GIT deposundaki dosyaları çıkarmak ve değiştirmek için bir ANT komut dosyası kullanıyorum, bu nedenle hiçbir üretim dosyasının üzerine yazılmayacağından eminim. ANT'de java özelliği dosyalarını değiştirmek için güzel bir seçenek var. Bu, yerel test değişkenlerinizi java tarzı bir özellik dosyasına koymak ve onu işlemek için bazı kodlar eklemek anlamına gelir, ancak bu size, sitenizi çevrimiçi olarak FTP'ye sokmadan önce oluşturmayı otomatikleştirme fırsatı verir. Genellikle üretim bilgilerinizi site.default.properties dosyasına koyarsınız ve ANT'nin ayarları yönetmesine izin verirsiniz. Yerel ayarlarınız site.local.properties içinde olacaktır.

    <?php
/**
 * This class will read one or two files with JAVA style property files. For instance site.local.properties & site.default.properties
 * This will enable developers to make config files for their personal development environment, while maintaining a config file for 
 * the production site. 
 * Hint: use ANT to build the site and use the ANT <propertyfile> command to change some parameters while building.
 * @author martin
 *
 */
class javaPropertyFileReader {

    private $_properties;
    private $_validFile;

    /**
     * Constructor
     * @return javaPropertyFileReader
     */
    public function   __construct(){
        $this->_validFile = false;
        return $this;
    }//__construct

    /**
     * Reads one or both Java style property files
     * @param String $filenameDefaults
     * @param String $filenameLocal
     * @throws Exception
     * @return javaPropertyFileReader
     */
    public function readFile($filenameDefaults, $filenameLocal = ""){

        $this->handleFile($filenameDefaults);
        if ($filenameLocal != "") $this->handleFile($filenameLocal);
    }//readFile

    /**
     * This private function will do all the work of reading the file and  setting up the properties
     * @param String $filename
     * @throws Exception
     * @return javaPropertyFileReader
     */
    private function handleFile($filename){

    $file = @file_get_contents($filename);

    if ($file === false) {
         throw (New Exception("Cannot open property file: " . $filename, "01"));
    }
    else {
        # indicate a valid file was opened
        $this->_validFile = true;

        // if file is Windows style, remove the carriage returns
        $file = str_replace("\r", "", $file);

        // split file into array : one line for each record
        $lines = explode("\n", $file);

        // cycle lines from file
        foreach ($lines as $line){
            $line = trim($line);

            if (substr($line, 0,1) == "#" || $line == "") {
                #skip comment line
            }
            else{
                // create a property via an associative array
                $parts   = explode("=", $line);
                $varName = trim($parts[0]);
                $value   = trim($parts[1]);

                // assign property
                $this->_properties[$varName] = $value;
            }
        }// for each line in a file
    }
    return $this;
    }//readFile

    /**
     * This function will retrieve the value of a property from the property list.
     * @param String $propertyName
     * @throws Exception
     * @return NULL or value of requested property
     */
    function getProperty($propertyName){
        if (!$this->_validFile) throw (new Exception("No file opened", "03"));

        if (key_exists($propertyName, $this->_properties)){
            return $this->_properties[$propertyName];
        }
        else{
          return NULL;
        }
    }//getProperty

    /**
     * This function will retreive an array of properties beginning with a certain prefix.
     * @param String $propertyPrefix
     * @param Boolean $caseSensitive
     * @throws Exception
     * @return Array
     */
    function getPropertyArray($propertyPrefix, $caseSensitive = true){
        if (!$this->_validFile) throw (new Exception("No file opened", "03"));

        $res = array();

        if (! $caseSensitive) $propertyPrefix= strtolower($propertyPrefix);

        foreach ($this->_properties as $key => $prop){
            $l = strlen($propertyPrefix);

            if (! $caseSensitive) $key = strtolower($key);

            if (substr($key, 0, $l ) == $propertyPrefix) $res[$key] = $prop;
        }//for each proprty

        return $res;
    }//getPropertyArray

    function createDefineFromProperty($propertyName){
        $propValue = $this->getProperty($propertyName);
        define($propertyName, $propValue);
    }//createDefineFromProperty


    /**
     * This will create a number of 'constants' (DEFINE) from an array of properties that have a certain prefix.
     * An exception is thrown if 
     * @param  String $propertyPrefix
     * @throws Exception
     * @return Array The array of found properties is returned.
     */
    function createDefinesFromProperties($propertyPrefix){
        // find properties
        $props = $this->getPropertyArray($propertyPrefix);

        // cycle all properties 
        foreach($props as $key => $prop){

            // check for a valid define name
            if (preg_match("'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'", $key)) {
                define($key, $prop);
            }   
            else{
                throw (new Exception("Invalid entry in property file: cannot create define for {" . $key . "}", "04"));
            }   
        }// for each property found

        return $props;
    }//createDefineFromProperty

}//class javaPropertyFileReader

sonra kullan:

  $props = new javaPropertyFileReader();
  $props->readFile($_SERVER["DOCUMENT_ROOT"] . "/lib/site.default.properties",$_SERVER["DOCUMENT_ROOT"] . "/lib/site.local.properties");

  #create one DEFINE
  $props->createDefineFromProperty("picture-path");

  # create a number of DEFINEs for enabled modules
  $modules = $props->createDefinesFromProperties("mod_enabled_");

Site.default.properties öğeniz şöyle görünür:

release-date=x
environment=PROD
picture-path=/images/

SITE_VERSION_PRODUCTION=PROD
SITE_VERSION_TEST=TEST
SITE_VERSION_DEVELOP=DEV

# Available Modules
mod_enabled_x=false
mod_enabled_y=true
mod_enabled_z=true

ve site.local.properties öğeniz şöyle görünecektir (ortam ve etkin modüller arasındaki farka dikkat edin):

release-date=x
environment=TEST
picture-path=/images/

SITE_VERSION_PRODUCTION=PROD
SITE_VERSION_TEST=TEST
SITE_VERSION_DEVELOP=DEV

# Available Modules
mod_enabled_x=true
mod_enabled_y=true
mod_enabled_z=true

Ve ANT talimatlarınız: ($ d {deploy} dağıtım hedefi dizininizdir)

<propertyfile
    file="${deploy}/lib/site.properties"
    comment="Site properties">
    <entry  key="environment" value="PROD"/>
    <entry  key="release-date" type="date" value="now" pattern="yyyyMMddHHmm"/>
</propertyfile>

1

Bugünlerde (2019) örneğin python / django'da ENV değişkenlerini kullanıyorum, bunlara varsayılanlar da ekleyebilirsiniz. Docker bağlamında, ENV değişkenlerini bir docker-compose.yml dosyasına veya sürüm kontrolünde yok sayılan ekstra bir dosyaya kaydedebilirim.

# settings.py
import os
DEBUG = os.getenv('DJANGO_DEBUG') == 'True'
EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST', 'localhost')

0

En basit çözüm, dosyayı varsayılanlara düzenlemek, işlemek ve ardından .gitignore. Bu şekilde, geliştiriciler bunu yaparken yanlışlıkla taahhüt etmezler git commit -a, ancak yine de varsayılanlarınızı değiştirmek istediğiniz (muhtemelen nadir) durumda bunu gerçekleştirebilirler git add --force.

Bununla birlikte, bir .defaultve .localyapılandırma dosyasına sahip olmak , sonuçta en iyi çözümdür, çünkü bu, makineye özgü bir yapılandırmaya sahip herkesin kendi yapılandırmasını bozmadan varsayılanları değiştirmesine izin verir.


Bu işe yaramaz - dosyalar izlenir ve .gitignoresonraya eklenirse , değişiklikler yine de izlenir.
Zeemee

0

Burada varsayılan ve yerel yapılandırma dosyalarıyla önerildiği gibi yapıyorum. Projelerde bulunan yerel yapılandırma dosyalarımı yönetmek .gitignoreiçin git deposu yaptım ~/settings. Orada tüm projelerden tüm yerel ayarlarımı yönetiyorum. Örneğin, bir klasör oluşturmak project1içinde ~/settingsve bunun içine bu proje için tüm yerel yapılandırma malzeme koymak. Bundan sonra bu dosyaları / klasörü project1.

Bu yaklaşımla yerel yapılandırma dosyalarınızı takip edebilir ve bunları normal kaynak kodu havuzuna koymayın.


0

@Greg Hewgill'in cevabına dayanarak, yerel değişikliklerinizle belirli bir taahhüt ekleyebilir ve bunu yerel değişim olarak etiketleyebilirsiniz:

git checkout -b feature master
vim config.local
git add -A && git commit -m "local commit" && git tag localchange

Ardından, özelliğinizin işlemlerini eklemeye devam edin. İşi bitirdikten sonra, bunu yaparak localchange commit olmadan bu dalı ana olarak birleştirebilirsiniz:

git rebase --onto master localchange feature
git fetch . feature:master
git cherry-pick localchange
git tag localchange -f

Bu komutlar:

1) Localchange taahhüdünü yok sayarak özellik dalınızı ana olarak yeniden başlatın. 2) Özellik dalından ayrılmadan hızlı ileri sarma 3) Localchange commit'i özellik dalının üstüne geri ekleyin, böylece üzerinde çalışmaya devam edebilirsiniz. Bunu, çalışmaya devam etmek istediğiniz başka herhangi bir şubeye yapabilirsiniz. 4) Localchange etiketini bu kirazla seçilmiş commit'e sıfırlayın, böylece rebase --ontoaynı şekilde tekrar kullanabiliriz .

Bu, en iyi genel çözüm olarak kabul edilen cevabın yerini almak anlamına gelmez, sorun hakkında kutunun dışında düşünmenin bir yolu olarak. Temelde yanlışlıkla sadece dan rebasing tarafından usta yerel değişiklikleri birleşmesini önlemek localchangeiçin featureve hızlı yönlendirme ustası.

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.