diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..7a53bb1 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,12 @@ +# Par défaut : tout changement requiert une review de ces personnes +* @MalikCherfi + +# Les workflows CI/CD ne peuvent être modifiés que par le lead DevOps +.github/workflows/ @MalikCherfi + +# Le fichier de dépendances requiert une validation technique +ressources/requirements.txt @MalikCherfi + +# Les fichiers de sécurité requièrent une double validation +.github/dependabot.yml @MalikCherfi +.github/CODEOWNERS @MalikCherfi diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..734d14d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,23 @@ +# dependabot.yml + +version: 2 + +updates: + # ── Mettre à jour les actions GitHub ────────────────────────────── + - package-ecosystem: "github-actions" + directory: "/" # cherche dans .github/workflows/ + schedule: + interval: "weekly" # vérifie chaque semaine + labels: + - "dependencies" + - "github-actions" + + # ── Mettre à jour les dépendances Python ────────────────────────── + - package-ecosystem: "pip" + directory: "/ressources" # cherche requirements.txt ici + schedule: + interval: "weekly" + labels: + - "dependencies" + - "python" + open-pull-requests-limit: 5 # max 5 PRs ouvertes en même temps diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a27357..8466b29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,70 @@ -name: CI +name: CI — NexaCloud API on: push: - branches: [ "main" ] + branches: [main] pull_request: - branches: [ "main" ] + branches: [main] jobs: - build: + pre-commit: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v6 - - name: Example step - run: echo "Add your build/test steps here!" + - name: Run pre-commit hooks + uses: pre-commit/action@v3.0.1 + + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: Installer flake8 + run: pip install flake8 + + - name: Lint avec flake8 + run: flake8 ressources/ --config ressources/.flake8 + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: Installer les dépendances + run: pip install -r ressources/requirements.txt + + - name: Tests with coverage + run: pytest ressources/ -v --cov=ressources --cov-report=term-missing + + - name: Generate the HTML report + run: pytest ressources/ --cov=ressources --cov-report=html + + - name: Upload the report + uses: actions/upload-artifact@v7 + with: + name: rapport-couverture + path: htmlcov/ + + # Mettre en cache les dépendances pip (accélère les builds suivants) + - name: Cache pip + uses: actions/cache@v5 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('ressources/requirements.txt') }} diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 0000000..1a901d1 --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,55 @@ +name: CI/CD — NexaCloud API + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + # ── Job 1 : Qualité ──────────────────────────────────────────────── + qualite: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - run: pip install -r ressources/requirements.txt + + - name: Lint + run: flake8 ressources/ --config ressources/.flake8 + + - name: Tests + run: pytest ressources/ -v --cov=ressources + + # ── Job 2 : Staging ─────────────────────────────────────────────── + staging: + runs-on: ubuntu-latest + needs: qualite + environment: staging + if: github.ref_name == 'main' + + steps: + - uses: actions/checkout@v6 + + - name: Deploy Staging on Azure App Service + run: | + echo "Deploying staging to Azure App Service..." + + # ── Job 3 : Production ──────────────────────────────────────────── + production: + runs-on: ubuntu-latest + needs: staging + environment: production + if: github.ref_name == 'main' + + steps: + - uses: actions/checkout@v6 + + - name: Deploy Production on Azure App Service + run: | + echo "Deploying production to Azure App Service..." diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..05470ef --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,22 @@ +name: Deploy + +on: + workflow_dispatch: + inputs: + environment: + description: "Choose the deployment environment" + required: true + default: "staging" + type: choice + options: + - staging + - production + +jobs: + deploy: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + + steps: + - name: "Déployer en ${{ inputs.environment }}" + run: echo "Déploiement en ${{ inputs.environment }}..." diff --git a/.github/workflows/hello.yml b/.github/workflows/hello.yml new file mode 100644 index 0000000..388af3f --- /dev/null +++ b/.github/workflows/hello.yml @@ -0,0 +1,27 @@ +name: Hello NexaCloud + +on: + push: + branches: [main] + workflow_dispatch: # permet de déclencher manuellement depuis l'interface GitHub + +jobs: + salutation: + runs-on: ubuntu-latest + + steps: + - name: Checkout du code + uses: actions/checkout@v6 + + - name: Informations sur l'environnement + run: | + echo "Repo : ${{ github.repository }}" + echo "Branche : ${{ github.ref_name }}" + echo "Commit : ${{ github.sha }}" + echo "Acteur : ${{ github.actor }}" + + - name: Lister les fichiers du repo + run: ls -la + + - name: Afficher la date et l'heure + run: date diff --git a/.github/workflows/secrets.yml b/.github/workflows/secrets.yml new file mode 100644 index 0000000..11cec49 --- /dev/null +++ b/.github/workflows/secrets.yml @@ -0,0 +1,20 @@ +name: Demo Secrets + +on: + workflow_dispatch: + +jobs: + demo: + runs-on: ubuntu-latest + + env: + API_KEY: ${{ secrets.API_KEY }} + + steps: + - name: Vérifier que le secret est défini + run: | + if [ -z "$API_KEY" ]; then + echo "❌ Le secret API_KEY n'est pas défini" + exit 1 + fi + echo "✅ Le secret API_KEY est défini (${#API_KEY} caractères)" diff --git a/.gitignore b/.gitignore index 78e7733..f0d695f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,9 +36,17 @@ override.tf.json .terraformrc terraform.rc +# Ignore setup script for git hooks if it is specific to local development and not needed in the repository +setup-hooks.sh + +# Ignore Python cache files and compiled bytecode, which are not necessary to include in version control +__pycache__/ +*.pyc + + # Optional: ignore graph output files generated by `terraform graph` # *.dot # Optional: ignore plan files saved before destroying Terraform configuration # Uncomment the line below if you want to ignore planout files. -# planout \ No newline at end of file +# planout diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..bb4ae21 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +repos: + - repo: https://github.com/pycqa/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + args: [--config, ressources/.flake8] + files: ressources/.*\.py$ + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-merge-conflict + - id: check-added-large-files + args: [--maxkb=500] + + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort # trie automatiquement les imports Python + files: ressources/.*\.py$ diff --git a/README.md b/README.md index 4abe121..b51e198 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # TP GitHub Actions — CI/CD et automatisation -**Durée estimée :** 6h -**Prérequis :** Git, GitHub, bases Bash ou PowerShell (TPs précédents) +**Durée estimée :** 6h +**Prérequis :** Git, GitHub, bases Bash ou PowerShell (TPs précédents) **Environnement :** tout OS avec Git installé et un compte GitHub actif --- @@ -198,11 +198,18 @@ Ouvrez le fichier `.github/workflows/ci.yml` déjà présent dans ce repo. Répondez aux questions suivantes **sans modifier le fichier** : 1. Sur quelle(s) branche(s) ce workflow se déclenche-t-il ? + - Le workflow se déclenche sur la branche "main" 2. Combien de jobs contient-il ? + - Il contient un job 3. Sur quel système d'exploitation tourne-t-il ? + - Il tourne sur ubuntu 4. Quelle action installe Python ? + - - uses: actions/setup-python@v5 + with: + python-version: '3.12' 5. Quelle commande lance les tests ? + Vérifiez vos réponses en allant dans l'onglet **Actions** de votre repo GitHub après votre premier push. --- @@ -242,7 +249,7 @@ Commitez et pushez. Observez l'exécution dans l'onglet **Actions**. > ✏️ **À vous** > -> Ajoutez un step qui affiche la date et l'heure du runner avec `date`. +> Ajoutez un step qui affiche la date et l'heure du runner avec `date`. > Puis déclenchez le workflow **manuellement** depuis l'interface GitHub (bouton "Run workflow").
diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..2c821cd --- /dev/null +++ b/notes.md @@ -0,0 +1 @@ +# Mon TP GitHub Actions diff --git a/ressources/app.py b/ressources/app.py index d7e75f2..e3885f4 100644 --- a/ressources/app.py +++ b/ressources/app.py @@ -38,5 +38,12 @@ def logs_critical(): return jsonify({"critical_count": seuil, "alerte": alerte}) +@app.route("/logs/error") +def logs_error(): + seuil = LOG_SUMMARY["error"] + alerte = seuil > 0 + return jsonify({"error_count": seuil, "alerte": alerte}) + + if __name__ == "__main__": app.run(debug=True, port=5001) diff --git a/ressources/requirements.txt b/ressources/requirements.txt index ff3cf3e..5df0e38 100644 --- a/ressources/requirements.txt +++ b/ressources/requirements.txt @@ -1,5 +1,5 @@ -flask==3.0.3 -pytest==8.2.0 -pytest-cov==5.0.0 -flake8==7.0.0 +flask==3.1.3 +pytest==9.0.3 +pytest-cov==7.1.0 +flake8==7.3.0 gunicorn==26.0.0 diff --git a/ressources/test_app.py b/ressources/test_app.py index ce56a43..be9e3dc 100644 --- a/ressources/test_app.py +++ b/ressources/test_app.py @@ -57,3 +57,13 @@ def test_logs_critical_alerte(client): assert "critical_count" in data assert "alerte" in data assert data["alerte"] is True + + +def test_logs_error_alerte(client): + """L'alerte est active quand il y a des erreurs.""" + response = client.get("/logs/error") + assert response.status_code == 200 + data = response.get_json() + assert "error_count" in data + assert "alerte" in data + assert data["alerte"] is True