Como você lida com arquivos de configuração no controle de origem?

Digamos que você tenha um aplicativo da Web típico e uma configuração de arquivo, seja qual for. Cada desenvolvedor trabalhando no projeto terá uma versão para suas checkboxs de desenvolvimento, haverá uma versão de desenvolvimento, produção e estágio. Como você lida com isso no controle de origem? Não checar este arquivo, verificar com nomes diferentes ou fazer algo extravagante?

O que eu fiz no passado é ter um arquivo de configuração padrão que é registrado no controle de origem. Em seguida, cada desenvolvedor possui seu próprio arquivo de configuração de substituição, que é excluído do controle de origem. Primeiro, o aplicativo carrega o padrão e, em seguida, se o arquivo de substituição estiver presente, carrega isso e usa quaisquer configurações da substituição, de preferência para o arquivo padrão.

Em geral, quanto menor o arquivo de substituição, melhor, mas sempre pode conter mais configurações para um desenvolvedor com um ambiente muito fora do padrão.

Não faça a versão desse arquivo. Versão de um modelo ou algo assim.

A configuração é código e você deve versioná-lo. Nós baseamos nossos arquivos de configuração em nomes de usuários; no UNIX / Mac e no Windows, você pode acessar o nome de login do usuário e, desde que sejam exclusivos do projeto, você está bem. Você pode até mesmo sobrescrever isso no ambiente, mas você deve controlar tudo.

Isso também permite examinar as configurações dos outros, o que pode ajudar a diagnosticar problemas de criação e plataforma.

Minha equipe mantém versões separadas dos arquivos de configuração para cada ambiente (web.config.dev, web.config.test, web.config.prod). Nossos scripts de implantação copiam a versão correta, renomeando-a para web.config. Desta forma, temos version control completo nos arquivos de configuração para cada ambiente, podemos facilmente realizar um diff, etc.

Atualmente eu tenho o arquivo de configuração “template” com uma extensão adicional, por exemplo:

web.config.rename 

No entanto, posso ver um problema com esse método se mudanças críticas foram alteradas.

A solução que usamos é ter apenas o único arquivo de configuração (web.config / app.config), mas adicionamos uma seção especial ao arquivo que contém configurações para todos os ambientes.

Há uma seção LOCAL, DEV, QA, PRODUÇÃO contendo cada uma das chaves de configuração relevantes para aquele ambiente em nosso (s) arquivo (s) de configuração.

O que faz tudo isso funcionar é um assembly chamado xxx.Environment que é referenciado em todos os nossos aplicativos (winforms e webforms) que informa ao aplicativo em que ambiente está operando.

O assembly xxx.Environment lê uma única linha de informações do machine.config da máquina em questão, que informa que está em DEV, QA, etc. Esta input está presente em todas as nossas estações de trabalho e servidores.

Espero que isto ajude.

+1 na abordagem de modelo.

Mas como essa questão tem a tag Git, vem à mente a alternativa distribuída, na qual as personalizações são mantidas em uma ramificação de teste privada:

 A---B---C---D--- <- mainline (public) \ \ B'------D'--- <- testing (private) 

Nesse esquema, a linha principal contém um arquivo de configuração genérico, "modelo", que exige que a quantidade mínima de ajustes seja funcional.

Agora, os desenvolvedores / testadores podem ajustar o arquivo de configuração para o conteúdo do seu coração, e só cometer essas mudanças localmente em um ramo de teste privado (por exemplo, personalizações B '= B +). Cada vez que mainline avança, eles facilmente mesclam em testes, o que resulta em commits de mesclagem como D '(= D + versão mesclada das customizações de B).

Este esquema realmente brilha quando o arquivo de configuração "template" é atualizado: as alterações de ambos os lados são mescladas e são extremamente prováveis ​​de resultar em conflitos (ou falhas de teste) se forem incompatíveis!

Eu usei o modelo antes, ou seja, web.dev.config, web.prod.config, etc, mas agora prefiro a técnica “replace arquivo”. O arquivo web.config contém a maioria das configurações, mas um arquivo externo contém valores específicos do ambiente, como conexões de database. Boa explicação no blog de Paul Wilson .

Acho que isso reduz a quantidade de duplicação entre os arquivos de configuração, o que pode causar problemas ao adicionar novos valores / atributos.

@Grant está certo.

Eu estou em uma equipe com cerca de 100 outros desenvolvedores, e nossos arquivos de configuração não são verificados no controle de origem. Temos versões dos arquivos no repository que são extraídos a cada check out, mas não são alterados.

Funcionou muito bem para nós.

Eu sempre mantive todas as versões dos arquivos de configuração no controle de origem, na mesma pasta que o arquivo web.config.

Por exemplo

 web.config web.qa.config web.staging.config web.production.config 

Eu prefiro esta convenção de nomenclatura (em oposição a web.config.production ou production.web.config) porque

  • Mantém os arquivos juntos quando você classifica por nome de arquivo
  • Mantém os arquivos juntos quando você classifica por extensão de arquivo
  • Se o arquivo for acidentalmente enviado para produção, você não poderá ver o conteúdo em http porque o IIS impedirá que os arquivos * .config sejam exibidos

O arquivo de configuração padrão deve ser configurado de forma que você possa executar o aplicativo localmente em sua própria máquina.

Mais importante, esses arquivos devem ser quase 100% idênticos em todos os aspectos, até mesmo na formatação. Você não deve usar guias em uma versão e espaços em outra para recuo. Você deve ser capaz de executar uma ferramenta de comparação nos arquivos para ver exatamente o que é diferente entre eles. Eu prefiro usar o WinMerge para diferenciar os arquivos.

Quando seu processo de criação cria os binários, deve haver uma tarefa que substitua o web.config pelo arquivo de configuração apropriado para esse ambiente. Se os arquivos estiverem compactados, os arquivos não relevantes deverão ser excluídos dessa compilation.

Eu controlo a versão, mas nunca a empurro para os outros servidores. Se o servidor de produção exigir uma alteração, faço essa alteração diretamente no arquivo de configuração.

Pode não ser bonito, mas funciona bem.

A versão check-in, plain-vanilla do app / web.config deve ser genérica o suficiente para funcionar em todas as máquinas do desenvolvedor e ser mantida atualizada com quaisquer novas alterações de configuração, etc. Se você precisar de um conjunto específico de configurações para o dev / teste / produção configurações, verifique em arquivos separados com essas configurações, como o GateKiller afirmou, com algum tipo de convenção de nomenclatura, embora eu normalmente vá com “web.prod.config”, para não alterar a extensão do arquivo.

Usamos um arquivo de configuração de modelo que é registrado no version control e, em seguida, uma etapa em nossa construção automatizada para replace inputs específicas no arquivo de modelo com configurações específicas do ambiente. As configurações específicas do ambiente são armazenadas em um arquivo XML separado que também está sob version control.

Estamos usando o MSBuild em nossa compilation automatizada, portanto, usamos a tarefa XmlUpdate das Tarefas da Comunidade do MSBuild para atualizar os valores.

Por muito tempo, fiz exatamente o que o bcwood fez. Eu mantenho cópias de web.dev.config, web.test.config, web.prod.config, etc. sob controle de origem e, em seguida, meu sistema de compilation / implementação os renomeia automaticamente à medida que são implementados em vários ambientes. Você recebe uma certa quantidade de redundância entre os arquivos (especialmente com todo o material do asp.net), mas geralmente funciona muito bem. Você também precisa garantir que todos na equipe se lembrem de atualizar todos os arquivos quando fizerem uma alteração.

A propósito, eu gosto de manter “.config” no final como a extensão para que as associações de arquivos não sejam quebradas.

No que diz respeito às versões locais do desenvolvedor do arquivo de configuração, eu sempre tento ao máximo incentivar as pessoas a usar as mesmas configurações locais o máximo possível, para que não haja necessidade de ter sua própria versão. Nem sempre funciona para todos, nesse caso, as pessoas normalmente só substituem localmente conforme necessário e partem de lá. Não é muito doloroso nem nada.

Em nosso projeto, temos a configuração armazenada em arquivos com um prefixo, em seguida, nosso sistema de compilation puxa a configuração apropriada com base no nome do host do sistema atual. Isso funciona bem para nós em uma equipe relativamente pequena, o que nos permite aplicar alterações de configuração aos arquivos de outras pessoas se / quando adicionarmos um novo item de configuração. Obviamente, isso definitivamente não se adapta a projetos de código aberto com um número ilimitado de desenvolvedores.

Apenas mantemos o check-in do arquivo de configuração de produção. É responsabilidade do desenvolvedor alterar o arquivo quando ele for retirado da fonte para fins de preparação ou desenvolvimento. Isso nos consumiu no passado, então eu não sugeriria isso.

Nós temos dois problemas aqui.

  • Em primeiro lugar, temos que controlar o arquivo de configuração fornecido com o software.

    É fácil para um desenvolvedor verificar um indesejado para alterar para o arquivo de configuração principal, se ele estiver usando o mesmo arquivo no ambiente de desconexão.

    Por outro lado, se você tiver um arquivo de configuração separado que seja incluído pelo instalador, é muito fácil esquecer de adicionar uma nova configuração a ele ou deixar que os comentários nele fiquem dessincronizados com os comentários da desconexão. configurando o arquivo.

  • Então, temos o problema de que os desenvolvedores precisam manter uma cópia do arquivo de configuração atualizada, à medida que outros desenvolvedores adicionam novas configurações. No entanto, algumas configurações, como strings de conexão de database, são diferentes para cada desenvolvedor.

  • Existe um terceiro problema que a pergunta / respostas não cobre. Como você mescla as alterações que um cliente fez em seu arquivo de configuração quando instala uma nova versão de seu software?

Eu ainda tenho que ver uma boa solução que funciona bem em todos os casos, no entanto, tenho visto algumas soluções parciais (que podem ser combinadas em diferentes combinações, conforme necessário), que reduz muito o problema.

  • Em primeiro lugar, reduza o número de itens de configuração que você tem em seu arquivo de configuração principal.

    Se você não tiver necessidade de permitir que seus clientes alterem seus mapeamentos, use o Fluent NHibernate (ou outro) para mover a configuração para o código.

    Da mesma forma para a configuração de injeção de debugging.

  • Divida o arquivo de configuração quando possível, por exemplo, use um arquivo separado para configurar o que o Log4Net registra.

  • Não repita itens entre vários arquivos de configuração, por exemplo, se você tiver 4 aplicativos da Web que estão todos instalados na mesma máquina, tenha um arquivo de configuração geral para o qual o arquivo web.config aponta.

    (Use um caminho relativo por padrão, então é raro ter que alterar o arquivo web.config)

  • Processe o arquivo de configuração de desenvolvimento para obter o arquivo de configuração de remessa.

    Isso pode ser feito com valores padrão nos comentários Xml que são definidos no arquivo de configuração quando uma compilation é feita. Ou ter seções que são excluídas como parte do processo de criação do instalador.

  • Em vez de apenas ter uma string de conexão com o database, tenha uma por desenvolvedor.

    Por exemplo, primeiro procure por “database_ianr” (onde ianr é meu nome de usuário ou nome de máquina) no arquivo de configuração em tempo de execução, se ele não for encontrado, procure por “database”

    Ter um segundo nível “, por exemplo, -oracle ou -sqlserver”, torna mais rápido para os desenvolvedores acessar ambos os sistemas de database.

    Isso também pode ser feito para qualquer outro valor de configuração.

    Em seguida, todos os valores que terminam em “_userName” podem ser distribuídos antes do envio do arquivo de configuração.

No entanto, no final, o que é você é um “proprietário do arquivo de configuração” que assume o gerenciamento responsável do (s) arquivo (s) de configuração, conforme descrito acima ou de outra forma. Ele / ela também deve fazer um diff no arquivo de configuração de faces do cliente antes de cada remessa.

Você não pode remover a necessidade de uma pessoa carinhosa com este problema.

Não acho que exista uma única solução que funcione para todos os casos, pois pode depender da sensibilidade dos dados nos arquivos de configuração, da linguagem de programação que você está usando e de muitos outros fatores. Mas acho importante manter os arquivos de configuração para todos os ambientes sob controle de código-fonte, para que você possa sempre saber quando foi alterado e por quem e, mais importante, recuperá-lo se as coisas derem errado. E eles vão.

Então aqui está como eu faço isso. Isso é para projetos de nodejs normalmente, mas acho que funciona para outros frameworks e idiomas também.

O que eu faço é criar um diretório configs na raiz do projeto e, sob esse diretório, manter vários arquivos para todos os ambientes (e algumas vezes separar os arquivos para o ambiente de cada desenvolvedor) que são rastreados no controle de origem. E há o arquivo real que o código usa a config nomeada na raiz do projeto. Este é o único arquivo que não é rastreado. Então parece com isso

 root | |- config (not tracked) | |- configs/ (all tracked) |- development |- staging |- live |- James 

Quando alguém faz o check out do projeto, ele copia o arquivo de configuração que deseja usar no arquivo de config monitorado e está livre para editá-lo como deseja, mas também é responsável por copiar essas alterações antes de se comprometer com os outros arquivos de ambiente conforme necessário.

E nos servidores, o arquivo não rastreado pode ser simplesmente uma cópia (ou referência) do arquivo rastreado correspondente a esse ambiente. Em JS, você pode simplesmente ter uma linha para exigir esse arquivo.

Este stream pode ser um pouco complicado no começo, mas tem grandes vantagens: 1. Você nunca precisa se preocupar com um arquivo de configuração sendo excluído ou modificado no servidor sem ter um backup 2. O mesmo se um desenvolvedor tiver alguma configuração personalizada em seu máquina e sua máquina pára de funcionar por qualquer motivo 3. Antes de qualquer implantação, você pode diferenciar os arquivos de configuração para development e staging por exemplo, e ver se há algo faltando ou quebrado.

Eu enfrentei o mesmo problema e encontrei uma solução para isso. Primeiro adicionei todos os arquivos ao repository central (também os do desenvolvedor).

Portanto, se um desenvolvedor buscar os arquivos do repository, a configuração do desenvolvedor também estará lá. Ao mudar para este arquivo, o Git não deve estar ciente dessas alterações. Dessa forma, as alterações não podem ser enviadas / confirmadas para o repository, mas permanecer localmente.

Eu resolvi isso usando o comando git: update-index --assume-unchanged . Eu fiz um arquivo bat que é executado na pré-construção dos projetos que estão contendo um arquivo cujas alterações devem ser ignoradas pelo Git. Aqui está o código que eu coloquei no arquivo bat:

 IF NOT EXIST %2%\.git GOTO NOGIT set fileName=%1 set fileName=%fileName:\=/% for /f "useback tokens=*" %%a in ('%fileName%') do set fileName=%%~a set "gitUpdate=git update-index --assume-unchanged" set parameter= "%gitUpdate% %fileName%" echo %parameter% as parameter for git "C:\Program Files (x86)\Git\bin\sh.exe" --login -i -c %parameter% echo Make FIleBehaveLikeUnchangedForGit Done. GOTO END :NOGIT echo no git here. echo %2% :END 

Na minha pré-construção eu fiz uma chamada para o arquivo bat, por exemplo:

 call "$(ProjectDir)\..\..\MakeFileBehaveLikeUnchangedForGit.bat" "$(ProjectDir)Web.config.developer" "$(SolutionDir)" 

Eu encontrei em SO um arquivo bat que copia o arquivo de configuração correto para o web.config / app.config. Eu também chamo esse arquivo bat na pré-construção. O código para este arquivo bat é:

 @echo off echo Comparing two files: %1 with %2 if not exist %1 goto File1NotFound if not exist %2 goto File2NotFound fc %1 %2 if %ERRORLEVEL%==0 GOTO NoCopy echo Files are not the same. Copying %1 over %2 copy %1 %2 /y & goto END :NoCopy echo Files are the same. Did nothing goto END :File1NotFound echo %1 not found. goto END :File2NotFound copy %1 %2 /y goto END :END echo Done. 

Na minha pré-construção eu fiz uma chamada para o arquivo bat, por exemplo:

 call "$(ProjectDir)\..\..\copyifnewer.bat" "$(ProjectDir)web.config.$(ConfigurationName)" "$(ProjectDir)web.config