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