Skip to content
Snippets Groups Projects
Verified Commit 1a0759df authored by Marco Aceti's avatar Marco Aceti
Browse files

Add 'Lezione 06'

parent 0982c21f
Branches
No related tags found
No related merge requests found
# Git workflow e strumenti
In git, l'utilizzo dei branch è fortemente incentivato dal suo design e dalle sue funzionalità, rendendo praticamente impossibile lavorare senza utilizzarli. I branch consentono di creare versioni separate del codice, permettendo di lavorare su diverse funzionalità o correzioni di bug in modo indipendente e senza interferire con il codice principale.
C'è __libertà completa__ sul loro utilizzo: tutti i branch hanno pari importanza, chiunque può crearli e nominarli in qualunque modo.
Lavorando in un team, è quindi necessario stabilire delle politiche sui tipi e i nomi di branch, in modo da organizzare il lavoro al meglio.
- [**GitFlow**](./01_gitflow.md): organizzazione branch
- [**Hosting centralizzato**](./02_hosting-centralizzato.md): repository centrali git
- [**Gerrit**](./03_gerrit.md): meccanismo di review in team
- [**Strumenti dell'opensource**](./04_strumenti-opensource/00_index.md): strumenti opensource per build automation e bug tracking
# Hosting centralizzato
Un hosting centralizzato Git è un servizio che fornisce una repository centrale per i progetti Git dove i contributi vengono integrati e gestiti, garantendo una maggiore trasparenza e controllo del processo di sviluppo e mantenendo molti vantaggi della decentralizzazione, come la possibilità di lavorare in modo asincrono e autonomo.
Gli hosting centralizzati come GitHub e GitLab, nella loro costante evoluzione, spesso inventano nuovi meccanismi e provano a imporre nuovi workflow, come il GitHub Flow o il GitLab Flow, per semplificare e ottimizzare il processo di sviluppo. Tuttavia, è importante valutare attentamente questi nuovi approcci e verificare se si adattano alle esigenze specifiche del progetto e della squadra di sviluppo.
Inoltre, molti servizi di hosting centralizzati offrono funzionalità aggiuntive, come la possibilità di eseguire il "fork" di un repository, inviare _pull request_ per le modifiche e di utilizzare strumenti di continuous integration (CI) per testare automaticamente le modifiche apportate al codice.
## Fork
Il "fork" di un repository Git è una __copia del repository originale__ che viene creata su un account di hosting diverso dal proprietario originale.
Questo permette a un altro sviluppatore di creare una copia del repository e di lavorare su di essa senza influire sul lavoro del proprietario originale e __senza la sua autorizzazione__.
È possibile quindi mantenere una _connessione_ tra i due repository e condividere facilmente le modifiche apportate.
La maggioranza delle piattaforme di hosting centralizzato __ottimizza la condivisione dello spazio degli oggetti__, utilizzando un'unica _repository fisica_ per tutti i fork.
Tuttavia, questo può comportare alcune problematiche di sicurezza, come ad esempio la difficoltà per la piattaforma di stabilire in quale fork si trova un determinato oggetto in caso di conflitto o la possibilità che un utente malintenzionato possa modificare o eliminare accidentalmente oggetti di altri fork.
Per questo motivo, è importante che le piattaforme implementino __misure di sicurezza adeguate__ per proteggere i dati dei fork e garantire la tracciabilità delle modifiche ([esempio sul kernel Linux](https://github.com/torvalds/linux/commit/b4061a10fc29010a610ff2b5b20160d7335e69bf)).
## Review / Pull request
Tra la creazione di una pull request e il suo _merge_, specialmente nei progetti open source (dove chiunque può proporre qualsiasi patch) è fondamentale prevedere un processo di __review__.
![Pull request](/assets/06_pull-request.png)
La funzionalità di _review/pull request_ permette di facilitare le interazioni tra gli sviluppatori utilizzando il sito di hosting come luogo comune per la discussione informale e la revisione delle modifiche.
## Continous integration (CI)
Come accennato in precedenza, molti servizi di hosting centralizzati offrono strumenti di __continuous integration__ (CI) che possono essere utilizzati per testare automaticamente le modifiche proposte nella pull request.
Questi strumenti consentono di verificare che le modifiche non introducano errori o vulnerabilità e di garantire che il codice sia pronto per essere integrato nel repository principale.
Possono essere utilizzati anche per eseguire automaticamente la _suite di test_ o automatizzare il deployment.
![CI/CD](/assets/06_ci-cd.png)
# Gerrit
Gerrit è un __sistema di review__ del codice sviluppato internamente da Google per gestire i progetti interni; si basa sul concetto di "peer review": tutti gli sviluppatori sono autorizzati a fare review delle proposte di modifica di qualsiasi zona di codice.
Nel processo di review di Gerrit, i __developer__ possono sottoporre proposte di cambiamento utilizzando un sistema di "patch" che descrive le modifiche apportate al codice.
I __reviewer__, ovvero gli altri sviluppatori del progetto, possono quindi esaminare le patch e decidere se accettarle o rifiutarle.
Una volta che una patch ha ricevuto un numero sufficiente di review positivi, viene automaticamente integrata nel __repository principale autoritativo__ in cui tutti hanno accesso in sola lettura.
Gerrit obbliga a strutturare le modifiche (_changeset_) in un unico commit (tecnica _squash_) al momento dell'accettazione.
Ciò significa che tutte le modifiche apportate devono essere fuse in un unico commit, in modo da rendere più facile la gestione del repository.
Al momento della review, invece, le modifiche rimangono separate in versioni singole, ovvero ogni modifica viene presentata come un commit separato, in modo che i reviewer possano esaminarle più facilmente.
## Verifier
Il verifier è uno strumento o un processo che viene utilizzato in Gerrit per verificare che le modifiche proposte siano corrette e funzionino come dovrebbero.
In particolare, il verifier scarica la patch, la compila, esegue i test e controlla che ci siano tutte le funzioni necessarie.
Se il verifier rileva dei problemi, può segnalarli al team di sviluppo perché vengano corretti prima che la patch venga accettata.
Una volta terminato il proprio processo, approva le modifiche votandole positivamente.
Solitamente sono necessari 1 o 2 voti per procedere.
## Approver
Una volta verificata, una proposta di modifiche deve essere anche approvata.
L'approvatore deve determinare la risposta alle seguenti domande riguardo la proposta di modifiche:
- _è valida per lo scopo del progetto?_
- _è valida per l'architettura del progetto?_
- _introduce nuove falle nel design che potrebbero causare problemi in futuro?_
- _segue le best practices stabilite dal progetto?_
- _è un buon modo per implementare la propria funzione?_
- _introduce rischi per la sicurezza o la stabilità?_
Se l'approver ritiene che la proposta di modifiche sia valida, può approvarla scrivendo "LGTM" (acronimo di _"Looks Good To Me"_) nei commenti della pull request.
# Strumenti dell'open source
Gli strumenti dell'open source sono una serie di programmi, librerie e servizi che vengono utilizzati per sviluppare progetti open source.
Questi strumenti sono pensati per semplificare il processo di sviluppo e gestione di progetti open source, rendendoli accessibili a una comunità di sviluppatori e contribuenti.
- [**Build automation**](./01_build-automation.md): `make`, Ant e Gradle
- [**Bug tracking**](./02_bug-tracking.md): tecniche di bug workflow
# Build automation
La build automation è un processo fondamentale nello sviluppo di software open source, che consiste nel creare un sistema automatizzato per compilare il codice sorgente in un eseguibile.
Questo processo è importante perché consente di risparmiare tempo e risorse, evitando di dover compilare manualmente il codice ogni volta che si apportano modifiche.
Inoltre, la build automation garantisce una maggiore qualità e coerenza del software, poiché il processo di compilazione viene eseguito in modo uniforme ogni volta.
## `make`
`make` è uno strumento di build automation che viene utilizzato per automatizzare il processo di compilazione di un progetto.
In particolare, `make` viene utilizzato per specificare come ottenere determinati _targets_ (obiettivi), ovvero file o azioni che devono essere eseguite, partendo dal codice sorgente.
Ad esempio, in un progetto di sviluppo software, un _target_ potrebbe essere il file eseguibile del programma, che viene ottenuto compilando il codice sorgente.
`make` segue la filosofia _pipeline_, ovvero prevede l'utilizzo di singoli comandi semplici concatenati per svolgere compiti più complessi.
È supportata la _compilazione incrementale_, ovvero il fatto di compilare solo le parti del progetto che sono state modificate dall'ultima volta, al fine di velocizzare il processo.
Inoltre, vengono gestite le _dipendenze_ tra file, ovvero le relazioni tra i diversi file che compongono il progetto: se un file sorgente dipende da un altro file, make assicura che il file dipendente venga compilato solo dopo che il file da cui dipende è stato compilato.
Ciò garantisce che il progetto venga compilato in modo coerente e che le modifiche apportate a un file siano considerate correttamente nella compilazione dei file dipendenti.
```make
CC=gcc
CFLAGS=-I.
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: hellomake.c hellofunc.o
$(CC) -o hellomake hellomake.o hellofunc.o $< $(CFLAGS)
```
Nell'esempio, se il _target_ hellomake (definito dai file `hellomake.c` e `hellofunc.o`) è stato aggiornato, occorre ricompilarlo utilizzando i comandi sotto.
Tuttavia, make lavora a un livello molto basso, il che può rendere facile commettere errori durante la sua configurazione e utilizzo.
Non c'è portabilità tra macchine (ambienti) diverse.
### `Makefile`
Un _Makefile_ è un file di testo che contiene le istruzioni per il programma make su come compilare e linkare i file sorgente di un progetto.
Ogni riga del Makefile definisce un obiettivo o una dipendenza, insieme ai comandi che devono essere eseguiti per raggiungerlo.
L'utilizzo del Makefile permette di automatizzare la compilazione e il linkaggio dei file sorgente, semplificando il processo di sviluppo di un progetto.
Nell'esempio menzionato, il Makefile definisce il target `hellomake`, che dipende dai file `hellomake.c` e `hellofunc.o`, e fornisce i comandi per compilarli e linkarli insieme.
### Generazione automatica
Sono stati creati dei tool (`automake`, `autoconf`, `imake`, ...) che _generano_ `Makefile` ad-hoc per l'ambiente attuale.
Il _mantra_:
```bash
$ ./configure
$ make all
$ sudo make install
```
era largamente utilizzato per generare un Makefile ad-hoc per l'ambiente attuale e installare il software sulla macchina in modo automatico.
`automake`, `autoconf`, e `imake` sono strumenti che aiutano a questo scopo, generando Makefile che possono essere utilizzati per compilare e installare il software in modo automatico.
## Ant
Ant nasce in Apache per supportare il progetto Tomcat.
Data una __definizione in XML__ della struttura del progetto e delle dipendenze invocava comandi programmati tramite classi Java per compilare il progetto.
Il vantaggio è che Java offre un livello d'astrazione sufficiente a rendere il sistema di build portabile su tutte le piattaforme.
Nella versione base supporta integrazioni con altri tool come CVS, Junit, FTP, JavaDOCS, JAR, ecc...
Non solo compila, ma fa anche deployment.
Il deployment consiste nell'installare e configurare un'applicazione o un sistema su uno o più server o ambienti di esecuzione.
Nel contesto di Ant, il deployment può includere l'invocazione di comandi per copiare i file del progetto sui server di destinazione, configurare le impostazioni di sistema o dell'applicazione, avviare o fermare servizi o processi, e così via.
In questo modo, Ant può essere utilizzato non solo per compilare il progetto, ma anche per distribuirlo e rendere disponibile l'applicazione o il sistema ai suoi utenti.
I target possono avere dipendenze da altri target.
I target contengono task che fanno effettivamente il lavoro; si possono aggiungere nuovi tipi di task definendo nuove classi Java.
Esempio di un build file:
```xml
<?xml version="1.0"?>
<project name="Hello" default="compile">
<target name="clean" description="remove intermediate files">
<delete dir="classes" />
</target>
<target name="clobber" depends="clean" description="remove all artifact files">
<delete file="hello.jar">
</target>
<target name="compile" description="compile the Java source code to class files">
<mkdir dir="classes" />
<javac srcdir="." destdir="classes" />
</target>
<target name="jar" depends="compile" description="create a Jar file for the application">
<jar destfile="hello.jar">
<fileset dir="classes" includes="**/*.class" />
<manifest>
<attribute name="Main-Class" value="HelloProgram" />
</manifest>
</jar>
</target>
</project>
```
## Gradle
Gradle è uno strumento di build automation che utilizza le repository Maven come punto di accesso alle librerie di terze parti.
Maven è una piattaforma di gestione delle dipendenze e della build automation per il linguaggio di programmazione Java.
Le repository Maven sono archivi online che contengono librerie Java, plugin e altri componenti utilizzati nella build di progetti Java.
Gradle utilizza queste repository per cercare e scaricare le librerie di cui ha bisogno per eseguire la build del progetto.
Gradle, che supporta Groovy o Kotlin come linguaggi di scripting, adotta un approccio dichiarativo e fortemente basato su convenzioni.
Ciò significa che tutto ciò che è già stato definito come standard non deve essere ridichiarato.
Inoltre, Gradle definisce un linguaggio specifico per la gestione delle dipendenze e permette di creare build multi-progetto.
Gradle scala bene in complessità: permette di fare cose semplici senza usare le funzioni complesse.
È estendibile tramite plugin che servono per trattare tool, situazioni, linguaggi legati solitamente al mondo Java.
### Plugin
I plugin servono per trattare tool, situazioni, linguaggi definendo task e regole per lavorare più facilmente.
Il plugin _Java_ definisce:
- una serie di __sourceSet__, ovvero dove è presente il codice e le risorse. Principalmente sono:
- `src/main/java`: sorgenti Java di produzione;
- `src/main/resources`: risorse di produzione;
- `src/test/java`: sorgenti Java di test;
- `src/test/resources`: risorse di test.
- dei __task__, anche con dipendenze tra loro.
![Task gradle](/assets/06_gradle-tasks.png)
### Altri plugin
- application, per l'esecuzione;
- FindBugs, jacoco: per la verifica e la convalida;
- eclipse, idea: per integrazione con gli IDE;
\ No newline at end of file
# Bug tracking
Il bug tracking è stato reso necessario nel mondo open source per via della numerosità dei contributi e della alta probabilità di avere segnalazioni duplicate.
Inoltre, per gestire le segnalazioni di bug nell'ambito dello sviluppo open source, esistono diversi strumenti come git-bug, BugZilla, Scarab, GNATS, BugManager e Mantis.
## Bug workflow
![Bug workflow](/assets/06_bug-workflow.png)
L'obiettivo del bug tracking è avere più informazioni possibili su ogni bug per saperli riprodurre e quindi arrivare a una soluzione.
È importante verificare i bug una volta che l'_issue_ è stato aperto, in modo da poter confermare la sua esistenza e la completezza delle informazioni fornite.
Un _issue_ è un problema o una richiesta di funzionalità segnalata all'interno di un progetto di software.
Gli issue vengono solitamente utilizzati per tenere traccia dei problemi noti o delle richieste di nuove funzionalità all'interno di un progetto, e possono essere gestiti attraverso un sistema di bug tracking o gestione delle richieste.
Gli issue possono essere aperti da qualsiasi membro del team o dalla comunità, e possono essere risolti o chiusi da un membro del team responsabile.
Ci sono diversi modi per cui può essere chiuso un bug:
- __duplicate__: quando è stato già segnalato in precedenza e quindi non rappresenta un problema nuovo. In questo caso, viene solitamente fatto riferimento al numero del bug originale che ha già ricevuto una risoluzione;
- __wontfix__: il bug viene chiuso come "non risolvibile" perché o rappresenta una funzionalità voluta dal progetto o è troppo complesso da risolvere per essere considerato conveniente farlo dal punto di vista dei progettisti;
- __can't reproduce__: non è stato possibile riprodurre il bug, ovvero che non è stato possibile ottenere lo stesso risultato o il comportamento segnalato dal bug. Ciò può essere dovuto a una mancanza di dettagli o a un errore nella segnalazione del bug stesso;
- __fixed__: il bug è stato fixato;
vs __fix verified__: il fix è stato integrato in una release passando tutti gli step di verifica.
...@@ -48,6 +48,14 @@ ...@@ -48,6 +48,14 @@
- [Meccanismo di base](./05_scm/02_meccanismo.md) - [Meccanismo di base](./05_scm/02_meccanismo.md)
- [git](./05_scm/03_git.md) - [git](./05_scm/03_git.md)
- [Git workflow e strumenti](./06_git-workflow/00_index.md)
- [GitFlow](./06_git-workflow/01_gitflow.md)
- [Hosting centralizzato](./06_git-workflow/02_hosting-centralizzato.md)
- [Gerrit](./06_git-workflow/03_gerrit.md)
- [Strumenti dell'opensource](./06_git-workflow/04_strumenti-opensource/00_index.md)
- [Build automation](./06_git-workflow/04_strumenti-opensource/01_build-automation.md)
- [Bug tracking](./06_git-workflow/04_strumenti-opensource/02_bug-tracking.md)
# 2. Progettazione e implementazione # 2. Progettazione e implementazione
# 3. Verifica e convalida # 3. Verifica e convalida
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment