Archief - [GIT / RoR] Deploying apps to customers & Deploy changes to customers

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

AliChemicali

Legacy Member
Hello,

I got a question about GIT and divide APPS to customers. I will start off with a scenario.
I have a repo in GIT with an App build in RoR, I currently deploy this app to customers via Capistrano.

Now Customers are buying the app so I create a seperated repo for these customers.

Since I don't have much experience with GIT I have 2 questions:

1) Some customers need custom developments where should I develop it? Because we do bug fixes and deploy them to customers but this custom code will be over written with every new deploy.

2) Currently we only have 1 customer so I just make developments in my Master Repo and merge it with the customer repo and do Cap deploy.

So as you can see I'm really stuck with this for example what if I have 10 customers with some developments that are custom to them. But they still must receive new Versions of the app and the custom code must still work.

So I'm looking for some advise.

VinceVe

Legacy Member
Om dit te doen:

Voor elke klant maak je een nieuwe branch en doet daar je aanpassingen op. Op de master doe je dan je patches, bugfixes, updates, etc. etc.

Om dan je andere branches te updaten ga in elke branch en doe een git rebase master.

AliChemicali

Legacy Member
VinceVe zei:
Om dit te doen:

Voor elke klant maak je een nieuwe branch en doet daar je aanpassingen op. Op de master doe je dan je patches, bugfixes, updates, etc. etc.

Om dan je andere branches te updaten ga in elke branch en doe een git rebase master.


Dus ik ga het even wat proberen te verduidelijk correct me if I am wrong.

- My Application branch(PROD) -- Current
- My Client branch(CLIENT) -- 3 ahead

Vervolgens is er een bug fix en dan gaat de situatie de volgende zijn:

- My Application branch(PROD) -- 1 ahead
- My Client branch(CLIENT) -- 3 ahead

Ik ga vervolgens naar de CLIENT Server ik log in en wat zal er gebeuren als ik doe "git rebase prod"?

Zo duidelijk mogelijk thanks!

bealzebub

Legacy Member
AliChemicali zei:
Ik heb een vraag over git en het verdelen van apps naar klanten. Ik zal starten van een bepaald scenario.
Ik heb een repo in git met een RoR app en deploy die naar klanten via Capistrano.

Goed zo ver, geautomatiseerd deployen via Capistrano (of iets gelijkaardig) zou door iedere developer met een beetje verstand moeten gedaan worden, PHP, RoR, Django of wat dan ook. Capistrano leent zich voor alles.

AliChemicali zei:
Nu klanten de app beginnen te kopen heb ik een aparte repo voor deze klanten gemaakt

Fout, je kan beter met git branches werken.

AliChemicali zei:
Omdat ik niet zoveel ervaring met git heb, heb ik twee vragen:

  1. Sommige klanten hebben klantspecifieke extras. Waar moet ik die juist steken? We doen immers bugfixes en deployen die naar alle klanten, maar de custom code zou overschreven worden bij elke deploy.
  2. Momenteel hebben we maar één klant, dus ontwikkel ik in de master repo en merge ik die met de klantrepo om vervolgens te deployen via Capistrano

Zoals je kunt zien zit ik volledig vast met dit voorbeeld. Wat als ik 10 klanten heb met specifieke ontwikkeling voor elk. Ze moeten nieuwe versies van de app kunnen krijgen, maar hun custom code moet nog steeds aanwezig zijn en werken.

De beste manier om hiermee om te gaan is dus in git je algemene ontwikkeling op een bepaalde branch te doen (dat mag gerust de master branch zijn) en je klantspecifieke code in een aparte branch te steken. Het voordeel daarvan is dat je je master kan mergen op het moment dat die stabiel is en de master kan deployen in bv. een staging omgeving. Sowieso zal het met custom code voor elke app belangrijk zijn van een goeie testsuite te voorzien. Het is immers goed mogelijk dat code die op de master juist werkt omwille van je aanpassingen op de klantenbranch niet meer correct werkt. T kan me nie veel schelen of je da nu met Test::Unit of met RSpec/Cucumber (vind ik persoonlijk aangenamer werken) doet, zolang ze er maar is en je code volledig dekt!

Voorbeeld:
Code:
git branch klant1
git branch klant2
git branch klant3
git checkout master
-- doe ontwikkeling en tests --
git checkout klant1
git merge master
-- enzovoort --

Nu, je voorbeeld laat nog wat onduidelijkheid bestaan over hoe je juist deployt. Deploy je die app telkens op een andere hosting account? (eventueel allemaal op dezelfde VPS/dedicated server) Scherm je die omgevingen van je klanten mooi af in hun eigen user en group zodat alles mooi secure is? Enzovoort.

Nu, voor het deployen zelf. De Capfile beschrijft eigenlijk hoe Capistrano te werk moet gaan. Uiteindelijk is dat gewoon een ruby script en je kan daarin prutsen zoveel je wil om het naar je hand te zetten. Wat ik zou doen is hetvolgende:

Je wil niet dat gans je serverconfiguratie en databasepaswoorden en dergelijk mee gedeployd worden. Wat wij daarom doen is een aparte repository "deploy" onderhouden. Die symlinken we dan in onze Rails app folder. Je steekt die "deploy" symlink in je .gitignore file zodat ie nie mee gecommit wordt. Ik ga ervan uit dat je in Linux of OS X werkt waar symlinks supereasy en parate kennis zouden moeten zijn, right? (t moet haast, ken eigenlijk niemand in Winblows wereld die Capistrano gebruikt of zelfs maar kent)

.gitignore in je rails app folder (bijvoorbeeld, bij te sturen waar nodig):
Code:
.DS_Store
log/*.log
log/*.txt
tmp/*
tmp/**/*
config/database.yml
db/*.sqlite3
db/schema.*
db/development_structure.sql
*.diff
bin/*
rerun.txt
.bundle
deploy
*.swp
*.sqlite

Nu je deploy directory afgeschermd is, kunnen we verder. Alles waar hierachter "mijnapp" staat vervang je door de naam van je applicatie

In je deploy dir maak je map "mijnapp" aan en een file aan die de naam draagt van je app, bv. "mijnapp.rb". Daarin heb je volgende code:

Code:
begin
  require 'capistrano_colors' 
rescue LoadError
  # Just ignore when the capistrano_colors gem is not installed.
end

# Custom CapFile that will load the appropriate deploy config file
# depending on the choice of clients.

require 'yaml'
require 'fileutils'

config_path = Pathname.new(__FILE__).parent.join('mijnapp')

instances = YAML.load_file(config_path.join('instances.yml'))

clients = instances.keys

if ARGV.include? "deploy"
  warn "I'm sorry, Dave. I'm afraid I can't do that. Use deploy:migrations instead."
  exit 1
end

puts "Clients:"
puts clients.join("\n")
puts "Deploy to:"
client = STDIN.gets.strip

# Let's allow a deploy to every single customer by using a wildcard (*)
if client == "*" # wildcard is used
  puts "Wildcard deploy: #{clients.join(', ')}"
  clients.each do |c|
    `echo #{c} | cap #{ARGV.join(' ')}`
  end
  exit(0)
else
  raise "invalid client" unless clients.include?(client)
  
  set :application, client
  set :instance, clients[client]
  load config_path.join("deploy")
end

In die "mijnapp" map heb je dan drie files: deploy.rb, instances.yml en database.yml.erb. De eerste bevat je deploy script dat je momenteel hebt voor capistrano (met enkele extra variabelen uit die bovengaande code erin verwerkt), de tweede bevat al de configuratie voor de verschillende klanten en de derde bevat een template voor de instellingen van je Rails app.

Je zal alles eens moeten doorlopen, maar je kan in ieder geval een uitgewerkt voorbeeld downloaden op https://dl.dropboxusercontent.com/u/10466685/mijnapp.zip

Zorg vooral da je begrijpt wat er allemaal aan de hand is en wat het doet. T is gewone Ruby code, nie veel speciaals aan, maar toch… Weet dat mijn voorbeeld zoals het nu uitgewerkt is zeker niet bruikbaar zal zijn, je zal zelf je deploy scripts moeten bekijken en de nodige aanpassingen doen. T zou zelfs kunnen dat er syntax errors enzo inzitten, k heb het gewoon uit de losse pols effe geschreven.

Hopelijks helpt het een beetje, t gaat er em vooral om te begrijpen wat Capistrano doet, hoe het de dingen doet en voor de rest Ruby te kennen (en niet alleen Rails).

bealzebub

Legacy Member
VinceVe zei:
Om dan je andere branches te updaten ga in elke branch en doe een git rebase master.

"git merge" is een pak veiliger dan "git rebase", zeker als je pusht naar een externe repo. K ga geen gans boek erover schrijven (al genoeg m'n best gedaan hierboven), maar google "git rebase versus merge" en je zal genoeg uitleg vinden. Ben in het verleden zelf al rare dingen tegengekomen met rebasen (zeker in teamverband), dus k vermijd het zoveel mogelijk (tenzij da k t gebruik waarvoor t echt bedoeld is). Rebase gebruiken kan destructieve gevolgen hebben. Dan heb k liever al die empty diff commits van een merge.

EDIT: nog één van de beste korte uiteenzettingen waar het kan fout lopen met rebase: http://www.jarrodspillers.com/2009/08/19/git-merge-vs-git-rebase-avoiding-rebase-hell/

AliChemicali

Legacy Member
Woow ik ga dit morgen eens tegoei lezen alvast bedankt voor de antwoorden !

AliChemicali

Legacy Member
bealzebub zei:
"git merge" is een pak veiliger dan "git rebase", zeker als je pusht naar een externe repo. K ga geen gans boek erover schrijven (al genoeg m'n best gedaan hierboven), maar google "git rebase versus merge" en je zal genoeg uitleg vinden. Ben in het verleden zelf al rare dingen tegengekomen met rebasen (zeker in teamverband), dus k vermijd het zoveel mogelijk (tenzij da k t gebruik waarvoor t echt bedoeld is). Rebase gebruiken kan destructieve gevolgen hebben. Dan heb k liever al die empty diff commits van een merge.

EDIT: nog één van de beste korte uiteenzettingen waar het kan fout lopen met rebase: git merge vs git rebase: Avoiding Rebase Hell - Jarrod Spillers


Ik ga even de deze quoten en wat meer informatie langs mijn kant geven:

- Ik deploy via capistrano, allee clients staan op 1 server ze hebben gewoon een andere database nodig.
- Ik werk op Mac.
- Kun je via capistrano ook automatisch de database laten maken per klant, deze naam moet dan uniek zijn anders gaat hij conflicten met de andere DB's.

AliChemicali

Legacy Member
Ik heb nog een bijkomende vraag:

1) I have a custom layout that is located in files: /app/assets/stylesheets/customizing.scss
Is it possible to setup this file only with the first deploy. Then in the future I should be able to only edit this customising without going on the server and change things. Is it possible to create another branch specific for this client with all the customizing.

-> het is moeilijk om uitleg hierover te geven. Ik heb custom layout file die moet mee gegeven worden met de eerste deploy. Maar niet met deploys in de future. In de future zou ik een andere deploy moeten kunnen doen die enkele deze file update.


2) Kun je bij een deploy:setup een random DB laten generen sinds ik meerdere customers ga hebben en anders ga ik database conflicten hebben.


3) Voor de goede werking van mijn code moet ik het volgende kunnen doen bij een deploty: rake db:migrate -> rake db:seed en dan nog eens een rake db:migrate. Heeft iemand een idee hoe je dit in de deploy file verwerkt.

bealzebub

Legacy Member
AliChemicali zei:
- Kun je via capistrano ook automatisch de database laten maken per klant, deze naam moet dan uniek zijn anders gaat hij conflicten met de andere DB's.

Dat kan, op voorwaarde da je een user in je db hebt die databases kan bijmaken en die dan vanuit je capistrano setup script gaat aanroepen. Ik vind het persoonlijk wel meer een taak van bv. Chef (Capistrano voor server setup en configuratie), maar het kan wel via capistrano. Zie verder voor de uitleg hoe.

AliChemicali zei:
1) I have a custom layout that is located in files: /app/assets/stylesheets/customizing.scss
Is it possible to setup this file only with the first deploy. Then in the future I should be able to only edit this customising without going on the server and change things. Is it possible to create another branch specific for this client with all the customizing.

-> het is moeilijk om uitleg hierover te geven. Ik heb custom layout file die moet mee gegeven worden met de eerste deploy. Maar niet met deploys in de future. In de future zou ik een andere deploy moeten kunnen doen die enkele deze file update.

Je hebt toch die capistrano hooks before en after?

Code:
after "deploy:cold" do
  # Kopieer die custom scss file hier
end

Maar ik denk dat je gewoon verkeerd denkt. Je moet goed begrijpen hoe capistrano te werk gaat:
  • SSH naar production server
  • Maak een nieuwe map aan met als naam iets in de zin van "mijnapp-4654564654" waarbij al die cijfers de huidige tijd zijn
  • Doe alles wat nog nodig is voor de app correct geupdate is
  • Wis de symlink "current" en maak een nieuwe die verwijst naar die nieuw uitgecheckte map
  • Herstart de app

Capistrano gaat dus effectief een volledig nieuwe kopie van je applicatie opzetten. Nu, je kan wel shared folders voorzien, maar die zijn eerder bedoeld voor log files en klantenuploads, niet voor wat jij wil doen.

Als het enkel om wa andere styling gaat en een apart databaseje, dan zou k trouwens gans die zever met aparte git branches enzo niet doen. Gewoon de afwijkende zaken in een gesymlinkte map en via capistrano de juiste zaken op de juiste plaats kopiëren bij deploy. Genoeg voorbeelden hoe je files overzet in m'n voorbeeldzip van vorige keer.

AliChemicali zei:
2) Kun je bij een deploy:setup een random DB laten generen sinds ik meerdere customers ga hebben en anders ga ik database conflicten hebben.

Random zou k nie doen. Je hebt daar in m'n voorbeeld van vorige keer die instance.yml file. Maak aan de hand van de data die daarin staat een nieuwe database aan. Je zal er wel nog een user key aan moeten toevoegen als je per db met een andere user wil werken.

Hieronder een voorbeeld voor mysql, maar met postgre kan je het ook doen mits je de command line call aanpast natuurlijk :p

Code:
desc "creates database & database user"
 
task :create_database do
  set :root_password, Capistrano::CLI.password_prompt("MySQL root password: ")
  set :db_user, instance[:user]
  set :db_pass, instance[:password]
  set :db_name, instance[:database]
    
  run "mysql --user=root --password=#{root_password} -e \"CREATE DATABASE IF NOT EXISTS #{db_name}\""
  run "mysql --user=root --password=#{root_password} -e \"GRANT ALL PRIVILEGES ON #{db_name}.* TO '#{db_user}'@'localhost' IDENTIFIED BY '#{db_pass}' WITH GRANT OPTION\""
 
end

AliChemicali zei:
3) Voor de goede werking van mijn code moet ik het volgende kunnen doen bij een deploty: rake db:migrate -> rake db:seed en dan nog eens een rake db:migrate. Heeft iemand een idee hoe je dit in de deploy file verwerkt.

Sure, gewoon een cap task maken die de rake uitvoert é. Je kan die dan in de capistrano hooks (bv. after :deploy) mee aanroepen.

Code:
namespace :deploy do
  desc "reload the database with seed data"
  task :seed do
    run "cd #{current_path}; rake db:seed RAILS_ENV=#{rails_env}"
  end
end

Alles wa k ik hier zeg kan je trouwens bijna letterlijk in de docs en de wiki op github terugvinden. Ik zei het vorige keer al, je moet eerst leren begrijpen hoe capistrano werkt, want anders ga je constant met dergelijke vragen blijven zitten. Begin met de docs en de wiki en desnoods loop je door de source code zelf.
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan