Impiego della tecnica push-to-deploy con Git

Spread the love

Questo articolo molto vecchio l’ho trovato originariamente nel blog di Kris Jordan ma mi è tornato utile adesso. Lo accomodo per il caso di mio interesse.

Ho un progetto in un host locale, un ambiente di produzione e un ambiente di staging al quale vorrei applicare in tempo reale le modifiche appena consegnate (commit) alla versione di sviluppo.

Questo aticolo, per arrivare a questo obiettivo, propone di creare un repository “bare” in remoto che serve solo per fare da tramite tra cartella di sviluppo in localhost e la cartella di deploy/stage in remoto, come illustrato nella figura 1

Fig. 1 Illustrazione della tecnica push – post – receive

Questa metodologia è stata inizalmente sviluppata da Heroku.

Preparazione dei repository

In questa versione personalizzata, a differenza dell’articolo di riferimento (nel quale si mette tutto in localhost):

  • lasceremo il repository git “myProject” in localhost;
  • creeremo il repository git “push” in mydomain.com
  • la directory “stage” ovviamente in mydomain.com

come illustrato in Fig. 1.

me@localhost:$ cd IdeaProjects/myProject

Ora dobbiamo spostarci in remoto e qui è indispensabile avere l’accesso shell al server.

Qui inizializziamo un repository detto “bare” (nudo) che avrà solo la funzione di passamano tra il Git locale e la directory di deploy in remoto

me@localhost:$ ssh myUser@mydomain.com
Password:
myUser@remotehost:$ git init --bare myProjectPush.git

Questo repository myProjectPush.git non conterrà il codice sorgente, bensì farà da passamano tra il client Git a bordo del nostro server di sviluppo e la cartella di staging “stage” nel server di staging

Ora nel Git locale dobbiamo creare un riferimento al repository remoto di push che chiameremo “push” (magari non è la scelta più felice… ma basta fare attenzione):

me@localhost:$  git remote add push myUser@mydomain.com:myProjectPush.git

Il nuovo repository remoto lo troviamo scritto nel file .git/config:

...
[remote "push"]
        url = myUser@mydomain.com:public_html/myProjectPush.git
        fetch = +refs/heads/*:refs/remotes/push/*

A questo dobbiamo istruire il Git remoto perché copi i file nella directory stage

Impostazione Push-to-Stage

Dopo aver creato il repository Git remoto dobbiamo scrivere un programma/script che istruisce git su come trattare i file che arrivano dal Git locale. Nella directory hooks della cartella myProjectPush.git ci sono gli script che Git lancia al verificarsi di un particolare evento. Ce ne sono già parecchi di esempio scritti in linguaggio bash, ma per questo scopo si può usare un qualsiasi linguaggio, nell’esempio che ho trovato è scritto in Ruby. Ma prima di tutto creiamo il file:

myUser@remotehost:$ cd myProjectPush.git/.git/hooks
myUser@remotehost:$ touch post-receive
myUser@remotehost:$ chmod +x post-receive

Il “gancio” che ci serve è quello che ci collega all’evento post-receive che è l’evento che accade quando il repository remoto riceve e accetta un push da parte di un repository di sviluppo. Quindi è necessario che il sistema operativo veda il file come uno script eseguibile.

Poi apriamo con l’editor che vogliamo il file post-receive e copiamo incolliamo questo programma Ruby (accertiamoci che l’interprete ruby sia installato con un comando “which ruby”)

#!/usr/bin/env ruby
# post-receive

# 1. Read STDIN (Format: "from_commit to_commit branch_name")
from, to, branch = ARGF.read.split " "

# 2. Only deploy if master branch was pushed
if (branch =~ /master$/) == nil
    puts "Received branch #{branch}, not deploying."
    exit
end

# 3. Copy files to deploy directory
deploy_to_dir = File.expand_path('../stage')
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f master`
puts "DEPLOY: master(#{to}) copied to '#{deploy_to_dir}'"

# 4.TODO: Deployment Tasks
# i.e.: Run Puppet Apply, Restart Daemons, etc

Vediamo cosa fa questo script:

  1. Quando viene attivato lo script post-receive, Git gli passa in STDIN tre parametri: ID commit HEAD precedente, ID commit HEAD presente e il nome del branch da trasferire. Assegnamo questi tre valori alle variabili from, to e branch.
  2. Facciamo in modo che vengano pushati solo commit sul master branch (quello principale); questo sarebbe ragionevole per un push finale verso un server di produzione, ma io adotterò questa tecnica solo per lo staging, quindi rilasso questa parte (posso fare anche push di branch di bugfix per esempio).
  3. La prossima cosa da fare è fare un checkout dal branch attuale alla cartelle “stage”, cioè una copia dei file fisici nella cartella stage.
  4. Il lavoro è finito; sarebbe possibile completare lo script con comandi che dovessero rendersi necessari, come un restart del web server (se ne abbiamo la facoltà) o cancellare file di cache. Ma intanto mi fermo qui.

Salviamo il file e vediamo cosa succede.

Test con Pushing

Possiamo testare facendo un commit e un push verso il remote “push”:

me@localhost:$ git commit -m 'Test push-to-stage'
me@localhost:$ git push push master
muser@mydomain.com's password: 
Counting objects: 510, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (477/477), done.
Writing objects: 100% (510/510), 18.16 MiB | 148.00 KiB/s, done.
Total 510 (delta 260), reused 0 (delta 0)
remote: Already on 'master'
remote: DEPLOY: master(915c38ce6cd49905ccff2abf4d9fea9a6ad61a3d) copied to '/home/myUser/domains/mydomain.com/public_html/stage'
To mydomain.com:public_html/myProjectPush.git
 * [new branch]      master -> master

Nell’output, le righe che iniziano con “remote:” sono quelle generate del nostro script! In particolare vediamo che è stata fatta la copia nella cartella stage.

That’s all folks!!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.