18 Commits

Author SHA1 Message Date
72c34f9b33 Merge pull request 'Update Jenkins pipeline setup instructions in README.' (#7) from update-doc into main
All checks were successful
rock-paper-scissors/pipeline/head This commit looks good
Reviewed-on: #7
2025-12-02 15:51:57 +01:00
Maxime Pierront
38a31b3e2b Update Jenkins pipeline setup instructions in README.
All checks were successful
rock-paper-scissors/pipeline/pr-main This commit looks good
2025-11-27 09:49:34 +01:00
9b1af5029f Merge pull request 'update-code' (#6) from update-code into main
All checks were successful
rock-paper-scissors/pipeline/head This commit looks good
Reviewed-on: #6
2025-11-26 17:06:38 +01:00
Maxime Pierront
9865e9dcc2 Sort stats by descending win count in StatBoardReponse constructor.
All checks were successful
rock-paper-scissors/pipeline/head This commit looks good
rock-paper-scissors/pipeline/pr-main This commit looks good
2025-11-26 16:58:06 +01:00
Maxime Pierront
ca34df2af9 Refactor test classes: remove redundant public modifiers & update test values. 2025-11-26 16:57:57 +01:00
b451ad2233 Merge pull request 'add-ci' (#5) from add-ci into main
Some checks failed
rock-paper-scissors/pipeline/head There was a failure building this commit
Reviewed-on: #5
2025-11-23 20:07:11 +01:00
Maxime Pierront
8a341c51ee update jenkins file and repo
All checks were successful
rock-paper-scissors/pipeline/head This commit looks good
rock-paper-scissors/pipeline/pr-main This commit looks good
2025-11-23 19:52:30 +01:00
Maxime Pierront
b4555aa73f Update Jenkinsfile to specify Maven 3.9 as required build tool.
All checks were successful
rock-paper-scissors/pipeline/head This commit looks good
2025-11-23 12:59:57 +01:00
8bf1594329 Merge pull request 'add-ci' (#4) from add-ci into main
Some checks failed
rock-paper-scissors/pipeline/head There was a failure building this commit
Reviewed-on: #4
2025-11-23 01:19:53 +01:00
Maxime Pierront
33b24c62d4 Refactor InMemoryStatRepository initialization with default stats constant, make Application.main public, update Java version to 21 in pom.xml, and remove redundant Jenkinsfile configuration.
Some checks failed
rock-paper-scissors/pipeline/pr-main There was a failure building this commit
rock-paper-scissors/pipeline/head There was a failure building this commit
2025-11-23 01:12:37 +01:00
Maxime Pierront
ac66320f9a Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages. 2025-11-23 00:59:37 +01:00
Maxime Pierront
71cc786ea5 Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages. 2025-11-23 00:48:59 +01:00
b8c9b382d8 Merge pull request 'Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages.' (#3) from add-ci into main
Some checks failed
rock-paper-scissors/pipeline/head There was a failure building this commit
Reviewed-on: #3
2025-11-23 00:40:16 +01:00
Maxime Pierront
56aa4c7b3a Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages. 2025-11-23 00:39:26 +01:00
56206c02bb Merge pull request 'Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages.' (#2) from add-ci into main
Reviewed-on: #2
2025-11-23 00:28:21 +01:00
Maxime Pierront
cb499eaf32 Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages. 2025-11-23 00:25:44 +01:00
9405b66593 Merge pull request 'Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages.' (#1) from add-ci into main
Reviewed-on: #1
2025-11-23 00:22:12 +01:00
Maxime Pierront
1d67074daa Add Jenkinsfile for CI/CD pipeline setup, including build, test, package, and deployment stages. 2025-11-23 00:19:06 +01:00
17 changed files with 288 additions and 41 deletions

65
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,65 @@
pipeline {
agent any
tools {
maven 'maven-3.9'
}
stages {
stage('Compilation') {
steps {
echo "Compilation du projet..."
sh 'mvn clean compile'
}
}
stage('Tests') {
steps {
echo "Exécution des tests..."
sh 'mvn test'
}
post {
always {
// Publier les résultats de tests JUnit dans Jenkins
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Packaging') {
steps {
echo "Packaging de l'application (sans exécuter les tests)..."
sh 'mvn package -DskipTests'
}
}
stage('Déploiement') {
steps {
sh '''
echo "=== Déploiement simple sur le port 8081 ==="
cd "$WORKSPACE"
# 1) Arrêter l'ancienne instance de CE jar (et pas tout java)
OLD_PIDS=$(pgrep -f "rock-paper-scissors-0.0.1-SNAPSHOT.jar" || true)
if [ -n "$OLD_PIDS" ]; then
echo "Arrêt des anciennes instances: $OLD_PIDS"
kill $OLD_PIDS || true
sleep 5
else
echo "Aucune ancienne instance à arrêter."
fi
# 2) Vérifier que le jar existe
JAR_FILE=target/rock-paper-scissors-0.0.1-SNAPSHOT.jar
if [ ! -f "$JAR_FILE" ]; then
echo "ERREUR : $JAR_FILE introuvable"
exit 1
fi
echo "Jar sélectionné : $JAR_FILE"
# 3) Démarrer en arrière-plan, en évitant que Jenkins tue le process
echo "Démarrage de l'application..."
JENKINS_NODE_COOKIE=dontKillMe nohup java -jar "$JAR_FILE" --server.port=8081 > app.log 2>&1 &
echo "Déploiement terminé (process lancé en arrière-plan)."
'''
}
}
}
}

177
README.md Normal file
View File

@@ -0,0 +1,177 @@
# Travaux Pratiques CI/CD
Le but de ce TP est dutiliser un git déjà existant et dy installer une multibranche pipeline Jenkins.
## Copier le git du TP sur son profil
Se rendre sur : https://grond.iut-fbleau.fr/pierront/rock-paper-scissors
Faire un fork
`/!\ ATTENTION NE PRENDRE QUE LA BRANCHE MAIN /!\`
## Créer la VM pour jenkins
### Créer la règle pare-feu pour accéder aux ports 8080 et 8081
Aller dans la section Pare-Feu
* Cliquer -> Créer une règle de pare-feu
* Nom : jenkins-rule
* Sens du trafic : Entrée
* Tag cibles : jenkins
* Plages IPv4 source : 0.0.0.0/0
* Cocher TCP
* Ports : 8080, 8081
* Puis créer
---
* Créer la VM
* Configuration de la machine
* Nom : jenkins
* Région : la plus proche
* Série : E2 / e2-medium
* Mise en réseau
* Cocher Autoriser le trafic HTTP
* Cocher Autoriser le trafic HTTPS
* Tags réseau : ajouter jenkins
* Créer la VM
---
## Installation de Jenkins sur la VM
Installer git
```bash
sudo apt install -y git
```
Installer Java
```bash
sudo apt install -y wget apt-transport-https gpg
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/adoptium.gpg > /dev/null
echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
sudo apt update
sudo apt install temurin-21-jdk
```
Installer Jenkins
```bash
sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
echo "deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc]" \
https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt update
sudo apt install jenkins
```
Verifier s'il a bien démarrer :
`systemctl status jenkins`
Se rendre sur l'adresse IP de la VM : http://<IP_EXTERNE_VM>:8080
Vous devriez voir la page d'accueil de Jenkins
![img.png](assets/jenkins_start.png)
* Suivre les instructions pour la creation de compte admin
* Selectionner : Install suggested plugins
* Créer un nouveau compte admin
* Voir cette page
![img.png](assets/jenkins_home.png)
---
## Configuration de Jenkins avec Gitea
### Installer le plugin Gitea
* Aller dans la section Manage Jenkins **(roue crantée)** -> Plugins
* Rechercher Gitea
* Installer le plugin
### Configurer Maven
* Aller dans la section Manage Jenkins **(roue crantée)** -> Tools
* Add Maven
* Nom : maven-3.9
* Cocher Install automatically
* Version : 3.9.11
* Cliquer sur Save
### Récupérer un token Gitea
* Se rendre sur Gitea
* Cliquer sur profil -> Settings -> Applications -> Generate new token
* Nom du jeton : jenkins-token
* Avec ces options :
![img.png](assets/gitea_token.png)
* Cliquer sur Generate token
* **Copier le token pour ne pas le perdre**
### Configurer Jenkins pour utiliser Gitea
* Aller dans la section Manage Jenkins -> System
* Add Gitea Server
* Choisir le nom du serveur (grond)
* Metter l'url du serveur : https://grond.iut-fbleau.fr
* Cocher Manage hooks
* Cliquer sur _Add +_
* Kind : Gitea Personal Access Token
* Token : celui copié dans la section précédente
* ID : gitea-token
* Cliquer sur Add
* Puis save
---
## Création d'un pipeline
* Aller sur l'accueil Jenkins -> New Item
* Nommer le projet : rock-paper-scissors
* Choisir le type de projet : Multibranch Pipeline
* Cliquer sur OK
* Add a source
* Gitea
* utiliser le jeton gitea-token
* owner : _<votre nom d'utilisateur gitea>_
* Choisir le projet : rock-paper-scissors
* Choisir le repository : https://grond.iut-fbleau.fr/pierront/rock-paper-scissors.git
* Cliquer sur Save
Voir que le scan se passe bien.
---
## Tester le pipeline
* Aller dans la section Build History -> Build Now
* Voir que le pipeline se passe bien
Tester l'application via cette url : http://<IP_EXTERNE_VM>:8081/swagger-ui/index.html
---
## Gestion du Jenkinsfile
* Regarder les étapes pour comprendre ce que fait le Jenkinsfile
---
## Ajouter le puits dans le code java
Le puits bat la pierre et les ciseaux.
Le puits est battu par la feuille.
Grace à notre pipeline on peut voir le si le code fonctionne bien via les tests unitaires.
Puis on peut tester réelement grace au deploiement sur la VM de l'application java.
Sources :
* https://adoptium.net/installation/linux/#deb-installation-on-debian-or-ubuntu
* https://www.jenkins.io/doc/book/installing/linux/#debianubuntu
* https://plugins.jenkins.io/gitea/
* https://www.jenkins.io/doc/book/pipeline/jenkinsfile/

BIN
assets/gitea_token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
assets/jenkins_home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
assets/jenkins_start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -27,7 +27,7 @@
<url/>
</scm>
<properties>
<java.version>25</java.version>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>

View File

@@ -6,7 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
static void main(String[] args) {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

View File

@@ -5,16 +5,17 @@ import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.domain.StatBo
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.spi.StatRepository;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Stub
public record InMemoryStatRepository(Map<String, Integer> stats) implements StatRepository {
private final static HashMap<String, Integer> DEFAULT_STATS = new LinkedHashMap<>(Map.of("joe", 1,"eoj", 3));
public InMemoryStatRepository() {
HashMap<String, Integer> stats1 = new HashMap<>();
stats1.put("joe", 1);
stats1.put("eoj", 3);
this(stats1);
this(DEFAULT_STATS);
}
@Override

View File

@@ -2,13 +2,16 @@ package fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.web;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.domain.StatBoard;
import java.util.Comparator;
import java.util.List;
public record StatBoardReponse(List<Stat> stats) {
public StatBoardReponse(StatBoard statBoard) {
this(statBoard.stats().entrySet().stream()
.map(Stat::fromMap)
.toList());
}
public StatBoardReponse(StatBoard statBoard) {
this(statBoard.stats().entrySet().stream()
.map(Stat::fromMap)
.sorted(Comparator.comparingInt(
stat -> -stat.wins()))
.toList());
}
}

View File

@@ -3,7 +3,7 @@ package fr.iut_fbleau.info.but3.automation.rock_paper_scissors;
import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;
public class ModulithStructureTests {
class ModulithStructureTests {
@Test
void verifyModularStructure() {

View File

@@ -26,7 +26,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
webEnvironment = RANDOM_PORT
)
@Import(FakeCpuConfiguration.class)
public class RockPaperScissorsPlayApplicationTest {
class RockPaperScissorsPlayApplicationTest {
@Autowired
private TestRestTemplate restTemplate;

View File

@@ -10,7 +10,7 @@ import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
public class MoveTest {
class MoveTest {
@Test
void should_check_move_size(){

View File

@@ -1,17 +1,16 @@
package fr.iut_fbleau.info.but3.automation.rock_paper_scissors.play.domain;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.play.RockPaperScissorsPlay;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.play.spi.FakeCpuPicker;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.domain.StatSaver;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.spi.stub.InMemoryStatRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class RockPaperScissorsPlayTest {

View File

@@ -14,7 +14,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@ApplicationModuleTest(
webEnvironment = RANDOM_PORT
)
public class StatApplicationTest {
class StatApplicationTest {
@Autowired
private TestRestTemplate restTemplate;
@@ -27,9 +27,9 @@ public class StatApplicationTest {
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(json.from(response.getBody())).extractingJsonPathArrayValue("@.stats").hasSize(2);
assertThat(json.from(response.getBody())).extractingJsonPathStringValue("@.stats[0].name").isEqualTo("joe");
assertThat(json.from(response.getBody())).extractingJsonPathNumberValue("@.stats[0].wins").isEqualTo(1);
assertThat(json.from(response.getBody())).extractingJsonPathStringValue("@.stats[1].name").isEqualTo("eoj");
assertThat(json.from(response.getBody())).extractingJsonPathNumberValue("@.stats[1].wins").isEqualTo(3);
assertThat(json.from(response.getBody())).extractingJsonPathStringValue("@.stats[0].name").isEqualTo("eoj");
assertThat(json.from(response.getBody())).extractingJsonPathNumberValue("@.stats[0].wins").isEqualTo(3);
assertThat(json.from(response.getBody())).extractingJsonPathStringValue("@.stats[1].name").isEqualTo("joe");
assertThat(json.from(response.getBody())).extractingJsonPathNumberValue("@.stats[1].wins").isEqualTo(1);
}
}

View File

@@ -3,6 +3,7 @@ package fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.domain.StatBoard;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.domain.StatBoardGetter;
import fr.iut_fbleau.info.but3.automation.rock_paper_scissors.stat.spi.stub.InMemoryStatRepository;
import java.util.LinkedHashMap;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
@@ -10,19 +11,20 @@ import java.util.HashMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class StatBoardGetterTest {
@Test
public void should_get_stat_board() {
HashMap<String, Integer> stats = new HashMap<>();
stats.put("joe", 1);
stats.put("eoj", 5);
InMemoryStatRepository statRepository = new InMemoryStatRepository(stats);
class StatBoardGetterTest {
StatBoardGet statBoardGet = new StatBoardGetter(statRepository);
@Test
void should_get_stat_board() {
HashMap<String, Integer> stats = new LinkedHashMap<>();
stats.put("eoj", 5);
stats.put("joe", 1);
InMemoryStatRepository statRepository = new InMemoryStatRepository(stats);
StatBoard statBoard = statBoardGet.board();
StatBoardGet statBoardGet = new StatBoardGetter(statRepository);
assertNotNull(statBoard);
assertEquals(stats, statBoard.stats());
}
StatBoard statBoard = statBoardGet.board();
assertNotNull(statBoard);
assertEquals(stats, statBoard.stats());
}
}

View File

@@ -20,7 +20,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@WebMvcTest(StatController.class)
@Import(StatConfiguration.class)
public class StatControllerTest {
class StatControllerTest {
private static final String STAT_ENDPOINT = "/stat";
@@ -36,8 +36,8 @@ public class StatControllerTest {
mockMvc.perform(get(STAT_ENDPOINT))
.andExpect(status().isOk())
.andExpect(jsonPath("$.stats").isArray())
.andExpect(jsonPath("$.stats[0].name").value("joe"))
.andExpect(jsonPath("$.stats[0].wins").value(1));
.andExpect(jsonPath("$.stats[0].name").value("eoj"))
.andExpect(jsonPath("$.stats[0].wins").value(3));
}
@TestConfiguration

View File

@@ -10,7 +10,7 @@ import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class StatSaverTest {
class StatSaverTest {
@ParameterizedTest(name = "name = {0}, initial = {1}, expected = {2}")
@CsvSource({"joe,0,1", "eoj,2,3"})
void should_save(String name, int initial, int expected) {