Creado: Forks y aportes a la comunidad authored by Santiago Martinez's avatar Santiago Martinez
# Tabla de contenidos:
* [Introducción](#introducción)
* [Orden tienen las cosas](#orden-tienen-las-cosas)
* [Manos a la obra](#manos-a-la-obra)
* [Crear el *fork* con su rama *upstream-master*](#crear-el-fork-con-su-rama-upstream-master)
* [Actualizar la rama *upstream-master* en el *fork*](#actualizar-la-rama-upstream-master-en-el-fork)
* [Comenzar a trabajar un nuevo feature/mejora](#comenzar-a-trabajar-un-nuevo-featuremejora)
* [¿Solicitud de fusión rechazada?](#solicitud-de-fusión-rechazada)
## Introducción
Al trabajar en la implementación de un nuevo servicio en Ansible, una tarea cotidiana y más que recomendable es buscar en [la galaxia](https://galaxy.ansible.com) *roles* públicos que ya implementen lo que se necesita.
Muchas veces encontramos uno o mas *roles* que cumplen a la perfección el 90% de lo que precisamos, pero les falta algún detalle menor o no son completamente compatibles con el sistema operativo que vamos a utilizar.
Lo natural es aprovechar el avance de la comunidad, partiendo de esa gran base de código, y agregar las características que se consideran necesarias. Una vez concretada la mejora, incorporamos el *role* a nuestro proyecto particular y **¡listo!**
¿Listo? ¿Qué sucede si luego el *role* original sigue evolucionando e incorpora algún feature o mejora de seguridad que nos interesa? ¿Y si la licencia del proyecto original obliga a recompartir el código (por ejemplo GNU-GPL)? **¿Por qué necesitas que te obliguen para compartir?** ¿Cómo gestionamos dos bases de código que bifurcaron y evolucionaron independientemente?
**Todo esto lo solucionamos creando y trabajando en *forks*.** Pero, ¿qué entendemos por *fork*?
> Crear un *fork* es producir una copia propia del repositorio de alguien más. Los *forks* actúan como una especie de vínculo entre el repositorio original y el propio. Se puede enviar solicitudes de fusión para ayudar a mejorar los proyectos de otros, proponiendo cambios al proyecto original sin necesidad de ser miembro.
Al realizar nuestras mejoras sobre un *fork*, mantenemos el histórico del repositorio original (en adelante *upstream*). Esto hace extremadamente fácil y práctico a los responsables del proyecto, integrar los cambios que podamos proponer en una solicitud de fusión (*merge/pull request*). Igual de práctico será para nosotros, integrar en el propio repositorio, los avances del *upstream*.
## Orden tienen las cosas
[Ansible Galaxy](https://galaxy.ansible.com) se integra solo con GitHub, por lo tanto todos los *roles* públicos se encuentran en repositorios de esta plataforma. Como consecuencia, los *roles* públicos en los que colaboramos, son *forkeados* en el namespace de [nuestra organización GitHub](https://github.com/UdelaRInterior).
Tratándose de una organización, muchas veces hay varios miembros del equipo *DevOps* trabajando en simultáneo sobre un mismo *fork*, eventualmente uno retoma el trabajo de otro. Acordamos entonces un flujo estándar de trabajo que nos permita mantener el código ordenado y optimizado para proponer solicitudes de fusión y para actualizarnos desde el *upstream*.
Este flujo es casi idéntico al [empleado para desarrollos propios](flujo-de-trabajo-con-Git-en-Gitlab-y-GitHub), trabajamos cada feature/mejora en una rama específica, con el agregado de una rama espejo (*mirror*) de *master* del *upstream*.
Esta rama espejo, denominada `upstream-master`, nos permite mantener en nuestro repositorio (el *fork*) una réplica exacta del *upstream*, desde la cual partir al crear cada nueva rama para trabajar un feature. Al mismo tiempo, nos permite mantener nuestra rama *master* libre, puediendo transformarse en una versión alternativa al *upstream*, en caso que nuestras solicitudes de fusión no sean aceptadas.
## Manos a la obra
Gestionar correctamente la rama `upstream-master` es una tarea sencilla, pero en la que hay que ser cuidadoso.
A continuación se documenta paso a paso los procedimientos correctos para:
### Crear el *fork* con su rama `upstream-master`
* Una vez localizado en GitHub el repositorio del *role* de interés, [crear el fork](https://guides.github.com/activities/forking/) en el namespace `UdelaRInterior`.
* Clonar el flamante repositorio *fork*. Bien puede ser mediante un simple `git clone`, o a través de un archivo `requirements.yml` y el utilitario [`ansible-galaxy` con la opción `-g`](entorno-de-desarrollo-operaciones#manual)
```bash
santiagomr@pc:~/interior$ git clone git@github.com:UdelaRInterior/ansible-zabbix-server.git
Clonando en 'ansible-zabbix-server'...
remote: Enumerating objects: 61, done.
remote: Counting objects: 100% (61/61), done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 1476 (delta 25), reused 44 (delta 15), pack-reused 1415
Recibiendo objetos: 100% (1476/1476), 273.62 KiB | 767.00 KiB/s, listo.
Resolviendo deltas: 100% (787/787), listo.
```
* Dentro del directorio clonado, podemos listar los *remotos* para comprobar que solo se encuentra el de `UdelaRInterior` bajo el nombre *origin*
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git remote -v
origin git@github.com:UdelaRInterior/ansible-zabbix-server.git (fetch)
origin git@github.com:UdelaRInterior/ansible-zabbix-server.git (push)
```
* Procedemos a agregar el remoto del repositorio *upstream*, y lo nombramos precisamente `upstream`. (La dirección remota del *upstream* lo obtenemos del mismo modo que si lo fuéramos clonar)
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git remote add upstream \
https://github.com/dj-wasabi/ansible-zabbix-server.git
```
Comprbamos el resultado:
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git remote -v
origin git@github.com:UdelaRInterior/ansible-zabbix-server.git (fetch)
origin git@github.com:UdelaRInterior/ansible-zabbix-server.git (push)
upstream https://github.com/dj-wasabi/ansible-zabbix-server.git (fetch)
upstream https://github.com/dj-wasabi/ansible-zabbix-server.git (push)
```
* "Parados" ahora en la rama *master* del *fork*, que todavía es una copia exacta de su homónima en el *upstream*, creamos la rama `upstream-master`:
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git branch -al
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git branch upstream-master
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git branch -al
* master
upstream-master
remotes/origin/HEAD -> origin/master
remotes/origin/master
```
* Nos "paramos" ahora en la nueva rama y la subimos a nuestro repositorio (*origin*), indicando el romoto y rama correcta explícitamente en el `push`
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git checkout upstream-master
Cambiado a rama 'upstream-master'
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git push origin upstream-master
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'upstream-master ' on GitHub by visiting:
remote: https://github.com/UdelaRInterior/ansible-zabbix-server/pull/new/upstream-master
remote:
To github.com:UdelaRInterior/ansible-zabbix-server.git
* [new branch] upstream-master -> upstream-master
```
Listo, ya tenemos nuestra rama espejo en local y subida a GitHub. Podemos comprobarlo listando las ramas locales y remotas:
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git branch -al
master
* upstream-master
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/upstream-master
```
### Actualizar la rama `upstream-master` en el *fork*
Eventualmente el proyecto *upstream* avanzará en su desarrollo y tendremos disponibles esos avances para actualizar nuestra rama *mirror*.
Con los remotos configurados tal como en el procedimiento anterior:
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git remote -v
origin git@github.com:UdelaRInterior/ansible-zabbix-server.git (fetch)
origin git@github.com:UdelaRInterior/ansible-zabbix-server.git (push)
upstream https://github.com/dj-wasabi/ansible-zabbix-server.git (fetch)
upstream https://github.com/dj-wasabi/ansible-zabbix-server.git (push)
```
El procedimiento de actualización consistirá en:
* "Pararse" en la rama `upstream-master`:
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git checkout upstream-master
Cambiado a rama 'upstream-master'
```
* Hacer `pull` desde *master* del remoto *upstream*:
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git pull upstream master
remote: Enumerating objects: 20, done.
remote: Counting objects: 100% (20/20), done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 20 (delta 7), reused 12 (delta 5), pack-reused 0
Desempaquetando objetos: 100% (20/20), listo.
Desde https://github.com/dj-wasabi/ansible-zabbix-server
* branch master -> FETCH_HEAD
* [nueva rama] master -> upstream/master
Actualizando 3d318f8..812a747
Fast-forward
.gitignore | 12 ++++++++++++
README.md | 11 +++++++----
defaults/main.yml | 6 +++++-
tasks/main.yml | 7 ++++---
tasks/mysql.yml | 24 ++++++++++++------------
5 files changed, 40 insertions(+), 20 deletions(-)
```
**Si venimos haciendo todo bien, las historias de `origin/upstream-master` y `upstream/master` serán identicas, por lo que el `pull` se completará automáticamente sin conflictos**
* Con la rama `upstream-master` ya actualizada en local, solo resta reflejar la actualización en `origin` (nuestro *fork*)
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git push origin upstream-master
Total 0 (delta 0), reused 0 (delta 0)
To github.com:UdelaRInterior/ansible-zabbix-server.git
3d318f8..812a747 upstream-master -> upstream-master
```
### Comenzar a trabajar un nuevo feature/mejora
Como ya se comentó, cualquier nuevo feature/mejora se trabajará en una rama específica. Como podrá sospecharse, dicha rama deberá crearse desde `upstream-master` previamente actualizado. De este modo partiremos lo más "limpios" posible para luego proponer el feature/mejora al proyecto *upstream*.
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git branch
master
* upstream-master
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git branch nuevo-feature-mejora
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git checkout nuevo-feature-mejora
Cambiado a rama 'nuevo-feature-mejora'
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git commit -am "Ahora hace el doble"
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git push origin nuevo-feature-mejora
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'nuevo-feature-mejora' on GitHub by visiting:
remote: https://github.com/UdelaRInterior/ansible-zabbix-server/pull/new/nuevo-feature-mejora
remote:
To github.com:UdelaRInterior/ansible-zabbix-server.git
* [new branch] nuevo-feature-mejora -> nuevo-feature-mejora
```
---
**Es importante tener en cuenta que una solicitud de fusión (*pull request* en GitHub), implica llevar una rama del *fork* a otra del *upstream*. Estas ramas quedan vinculadas durante todo el ciclo de vida de las solicitud de fusión, por lo tanto cualquier commit que se agregue a la rama propuesta será anexado a la solicitud de fusión**. Esta es otra razón por la que trabajar asuntos independientes en ramas independientes. De lo contrario los solicitudes de fusión que se envíen serán muy confusas, reduciendo las posibilidades de ser aceptada.
---
### ¿Solicitud de fusión rechazada?
Eventualmente, si los features/mejoras propuestas no son aceptadas, querremos utilizarlos de todas formas. En este escenario, que debemos evitar siempre que sea posible, podremos fusionar esas ramas en nuestro propio *master*, y pasar a utilizar el *role* en la "versión del *fork*". Análogamente, podremos seguir integrando las mejoras que se agreguen en *upstream*.
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git checkout master
Ya en 'master'
Tu rama está actualizada con 'origin/master'.
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git merge nuevo-feature-mejora
Actualizando 5dyd4o2..4a75nd7
Fast-forward
defaults/main.yml | 9 ++++++-
tasks/mysql.yml | 37 +++++++++++++++++++++++++++++++++++----------
2 files changed, 56 insertions(+), 23 deletions(-)
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git merge upstream-master
Actualizando 4a42c3f..812a747
Fast-forward
.gitignore | 12 ++++++++++++
README.md | 12 ++++++++----
defaults/main.yml | 7 ++++++-
tasks/main.yml | 25 ++++++-------------------
4 files changed, 67 insertions(+), 34 deletions(-)
```
```bash
santiagomr@pc:~/interior/ansible-zabbix-server$ git push origin master
Total 0 (delta 0), reused 0 (delta 0)
To github.com:UdelaRInterior/ansible-zabbix-server.git
4a42c3f..812a747 master -> master
```
\ No newline at end of file