Déploiement sémantique avec Gitlab CI et To-Be-Continuous

Automatisez entièrement votre flux de travail de versionnage sémantique avec GitLab CI, semantic-release et to-be-continuous. Fini les erreurs humaines dans le versionnage, fini les modifications manuelles : gain de temps et d’énergie assurés.

Déploiement sémantique avec Gitlab CI et To-Be-Continuous
Photo par Daniil Komov / Unsplash

Introduction à la libération sémantique

Imaginons que vous soyez ingénieur logiciel, ingénieur plateforme ou tout simplement un professionnel de l'informatique. Vous suivez les modifications apportées à vos créations (applications web/mobiles, fichiers Docker, manifestes Kubernetes, scripts, documentation, etc.) via des plateformes de gestion de versions comme GitLab et travaillez en équipe ou peut-être seul.

Vous avez adopté Versionnage sémantique mais vous pensez encore à la prochaine version que vous allez publier à chaque fois et vous effectuez des mises à jour manuelles de version, ce qui demande du temps et des efforts supplémentaires.

Pour résoudre ce problème, dites bonjour à libération sémantiqueSemantic-release déterminera automatiquement le numéro de version de votre prochaine publication, mettra à jour les fichiers de version dans vos sources, créera/mettra à jour un fichier de journal des modifications contenant les types de modifications (correction de bug, fonctionnalité, changement incompatible) et les commits associés, créera et publiera des versions avec leurs notes de version et ressources associées.

journal des modifications de la version sémantique.webp

aperçu des balises Git de la version sémantique.webp

Tout cela est possible car semantic-release analyse vos commits. Ils doivent donc être formatés d'une manière spécifique. Conventions des messages de commit Angular est utilisé par défaut par semantic-release et est très similaire à Engagement conventionnel que vous pourriez également utiliser.

Ensuite, je vais vous montrer une façon simple de mettre en œuvre libération sémantique avec Gitlab CI et l'open source à venir projet, qui contient des modèles et des composants Gitlab CI pour faciliter la création de pipelines.

Le projet d'exemple

Imaginons que je travaille sur le packaging et la distribution des manifestes Kubernetes pour ma toute nouvelle application open source.

J'utilise Helm pour créer les paquets et Gitlab pour le versionnage des sources et également comme registre OCI pour la distribution des paquets.

J'ai créé le dépôt GitLab et les fichiers sources Helm nécessaires pour empaqueter et distribuer les manifestes Kubernetes. Voici le contenu du dépôt à cette étape :

.
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── httproute.yaml
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

Nous allons implémenter la publication sémantique sur ce projet.

Configuration Gitlab CI

Je souhaite maintenant utiliser la fonctionnalité d'intégration continue (CI) de GitLab pour implémenter la publication sémantique et automatiser le linting, la création de packages et la publication de mon graphique Helm lors de chaque modification du dépôt. Pour cela, je dois créer un fichier de configuration « .gitlab-ci.yml » avec le contenu suivant :

include:
  - component: $CI_SERVER_FQDN/to-be-continuous/semantic-release/gitlab-ci-semrel@4.1
    inputs:
      # Make semantic-release job runs automatically, manual by default
      auto-release-enabled: true

  - component: $CI_SERVER_FQDN/to-be-continuous/helm/gitlab-ci-helm@9.4
    inputs:
      # Do not add any Helm repos
      repos: ""
      # Enable Charts linting
      lint-disabled: false
      # Run the helm-publish job only on tags pipelines
      publish-on: tag

# Make the helm-package job runs only on tags pipelines
helm-package:
  rules:
    - if: $CI_COMMIT_TAG

# Make the helm-lint job runs only on default 
# branches and Merge Requests pipelines  
helm-lint:
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Nous utilisons deux composants Gitlab CI issus de l'open source à venir projet:

Si les commits de vos demandes de fusion ne sont pas transférés après la fusion, vous pouvez activer l'option « Fast Forward » pour les demandes de fusion dans GitLab (Paramètres > Demandes de fusion > Paramètres de transfert rapide des commits). Cela ajoutera l'historique des commits de votre demande de fusion à la branche cible après la fusion, au lieu de créer un nouveau commit global « Fusionner la branche… dans la branche principale ». Ainsi, semantic-release aura accès à vos commits habituels après la fusion et pourra mettre à jour les versions conformément à votre configuration.

Configuration de la version sémantique

J'utilise également un fichier de configuration '.releaserc.yml' qui sera utilisé par le composant semantic-release (gitlab-ci-semrel), pour configurer semantic-release. Voici le contenu de ce fichier :

# Run the semantic-release job only on these branches
branches:
  - main
  - master

plugins:
  # Analyzes commit messages to determine the next version number.
  # https://github.com/semantic-release/commit-analyzer
  - '@semantic-release/commit-analyzer'

  # Generates release notes from commit messages.
  # https://github.com/semantic-release/release-notes-generator
  - '@semantic-release/release-notes-generator'

  # Creates or updates a changelog file.
  # https://github.com/semantic-release/changelog
  - '@semantic-release/changelog'

  # Executes a shell command to update the Helm chart version.
  # This requires `yq` to be installed in the CI environment.
  # The to-be-continuous/semantic-release component we are using includes it.
  # https://github.com/semantic-release/exec
  - - '@semantic-release/exec'
    - prepareCmd: 'yq e ''.version = "${nextRelease.version}"'' -i Chart.yaml && yq e ''.appVersion = "${nextRelease.version}"'' -i Chart.yaml'

  # Commits release assets to the Git repository.
  # https://github.com/semantic-release/git
  - - '@semantic-release/git'
    - assets:
        - CHANGELOG.md
        - Chart.yaml
      message: "chore(release): ${nextRelease.version} [skip ci on prod]\n\n${nextRelease.notes}"

  # Publishes a GitLab release.
  # https://github.com/semantic-release/gitlab
  - '@semantic-release/gitlab'

# Set the Git tags format to 1.0.0 instead of v1.0.0
tagFormat: "${version}"

Le travail de libération sémantique de la gitlab-ci-semrel Le composant recherchera un .releaserc.{json,yaml,etc} dans le dépôt. Si le fichier de configuration est trouvé, il est utilisé.

Sinon, la configuration sera générée à la volée par la tâche de publication sémantique à l'aide des paramètres de configuration disponibles sous forme de entrées du système gitlab-ci-semrel composant.

Voici d'autres liens utiles qui peuvent vous aider à configurer le comportement de semantic-release via le fichier de configuration '.releaserc' :

Octroi des autorisations d'écriture au dépôt semantic-release

Dernière chose… Il faut accorder à la tâche de publication sémantique les autorisations de lecture, de création et de mise à jour des fichiers dans le dépôt GitLab. Elle doit également pouvoir effectuer des commits et créer des tags. Pour cela, il est nécessaire de lui fournir un jeton d'accès en définissant la variable d'environnement « GITLAB_TOKEN ».

Voici trois façons de générer une valeur pour la variable « GITLAB_TOKEN » :

Voici les autorisations que vous devez accorder au jeton d'accès :

  • api - accorde un accès en lecture et en écriture à l'API
  • read_repository - accorde un accès en lecture au dépôt
  • write_repository - accorde un accès en écriture au dépôt

Une fois le jeton d'accès généré avec les autorisations requises, ajoutez la variable « GITLAB_TOKEN » en tant que variable Gitlab CI/CD dans « Paramètres -> CI/CD -> Variables ».

Les avantages de cette configuration

Permettez-moi maintenant d'expliquer les avantages de cette configuration pour mon flux de travail de packaging et de distribution Helm Chart.

Je n'ai plus à me soucier de la version à définir pour le graphique après une modification. Le processus de mise à jour de la version du graphique et de publication de cette nouvelle version en tant qu'artefact OCI est entièrement automatisé.

Dans les cas où une mise à jour de version est nécessaire, la tâche de publication sémantique mettra à jour les fichiers de version requis dans le dépôt et créera des étiquettes Git associées et des versions nommées avec cette nouvelle version.

S'il n'y a pas d'étiquettes Git dans le dépôt, le premier numéro de version créé par semantic-release sera 1.0.0. S'il existe déjà des étiquettes, c'est le numéro de version de la dernière étiquette existante qui sera incrémenté.

Voici des exemples de tags et de versions Gitlab associées, créées par la tâche semantic-release :

semantic-release-git-tags.webp

Voici un aperçu du contenu d'une publication :

aperçu des balises Git de la version sémantique.webp

Le seul effort qu'il me reste à faire est de bien configurer mes messages de commit en fonction du type de modification que j'effectue (versioning sémantique (majeure, mineure ou corrective) dans le dépôt. Je devrais préfixer mes messages de commit par :

  • fix: — Pour les correctifs (bugs, sécurité, etc.) : le comportement actuel du graphique reste inchangé. La version « patch » du graphique sera incrémentée par semantic-release.

  • feat: Lors de l'ajout de nouvelles fonctionnalités : le comportement actuel du graphique est modifié. La version mineure du graphique sera alors incrémentée par semantic-release. L'ajout d'un point d'exclamation (par exemple, « feat ! ») ou la création d'un commit dont le pied de page inclut « CHANGEMENT IMPORTANT » entraînera l'incrémentation de la version majeure du graphique par semantic-release.

  • chore:, doc:, ci: — pour les modifications qui ne doivent pas déclencher de mise à jour de version. Cela empêchera « semanctic-release » de mettre à jour la version.

Cette convention de commit est appelée Engagements conventionnels.

Flux de travail final

Voici un aperçu du flux de travail complet de packaging et de distribution des graphiques.

1 - J'apporte des modifications au dépôt dans une nouvelle branche. Je veille à ce que mes commits respectent les règles. Engagements conventionnels.

2 - Je crée une demande de fusion pour intégrer les modifications à la branche principale. Un pipeline GitLab CI exécute la tâche « helm-lint ». La demande de fusion est ensuite examinée.

pipeline de requêtes de fusion de publication sémantique.webp

3 - Une fois la demande de fusion approuvée, je procède à la fusion. Un pipeline Gitlab CI exécute la tâche « helm-lint » suivie de la tâche « semantic-release » sur la branche « main ».

pipeline de branche principale de publication sémantique.webp

La tâche « semantic-release » analyse les commits et met à jour les versions sémantiques du graphique dans le fichier « Chart.yaml » en conséquence :

  • Une mise à jour de version « patch » est effectuée uniquement si elle détecte des commits commençant par « fix ».

  • Une mise à jour de version « mineure » est effectuée si elle détecte au moins un commit commençant par « feat » et aucun commit commençant par « feat! » ou contenant un pied de page avec « CHANGEMENT IMPORTANT ».

  • Une mise à jour de version « majeure » est effectuée si elle détecte au moins un commit commençant par « feat! » ou contenant un pied de page avec « CHANGEMENT IMPORTANT ».

mise à jour de la version du graphique.webp

4 - La tâche de publication sémantique crée ou met à jour un fichier 'CHANGELOG.md' contenant les types de modifications (corrections de bogues, etc.) et des liens vers les commits associés.

journal des modifications de la version sémantique.webp

5 - La tâche de publication sémantique crée un nouveau commit dans la branche principale, contenant ses modifications sur « CHANGELOG.md » et « Chart.yml ». Le corps de ce commit contient une note de version ajoutée au corps du message de commit.

semantic-release-commit-in-main.webp

Le titre du message de commit inclut '[skip ci on prod]' pour garantir que le commit créé par la tâche semantic-release dans la branche principale ne crée pas un nouveau pipeline de branche Gitlab CI, mais ne crée un pipeline qu'après la création d'une étiquette.

6 - La tâche de publication sémantique crée une étiquette portant le nouveau numéro de version uniquement lors de l'application d'un correctif, d'une mise à jour mineure ou majeure. La création de cette étiquette déclenche l'exécution, par un pipeline GitLab CI, de la tâche « helm-package », suivie de la tâche « helm-publish » qui publie les artefacts Helm Charts dans le registre OCI de GitLab.

pipeline d'étiquettes de publication sémantique.webp

Voici un exemple de sortie des journaux de la tâche semantic-release dans laquelle une mise à jour de version « patch » a été effectuée de la version 1.0.3 à la version 1.0.4 :

[semantic-release] › ℹ  Running semantic-release version 25.0.3
[semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/changelog"
[semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/git"
[semantic-release] › ✔  Loaded plugin "verifyConditions" from "@semantic-release/gitlab"
[semantic-release] › ✔  Loaded plugin "analyzeCommits" from "@semantic-release/commit-analyzer"
[semantic-release] › ✔  Loaded plugin "analyzeCommits" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "verifyRelease" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "generateNotes" from "@semantic-release/release-notes-generator"
[semantic-release] › ✔  Loaded plugin "generateNotes" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/changelog"
[semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "prepare" from "@semantic-release/git"
[semantic-release] › ✔  Loaded plugin "publish" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "publish" from "@semantic-release/gitlab"
[semantic-release] › ✔  Loaded plugin "addChannel" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "success" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "success" from "@semantic-release/gitlab"
[semantic-release] › ✔  Loaded plugin "fail" from "@semantic-release/exec"
[semantic-release] › ✔  Loaded plugin "fail" from "@semantic-release/gitlab"
[semantic-release] › ✔  Run automated release from branch main on repository https://gitlab-ci-token:[secure]@gitlab.com/hackerstack/semantic-release-demo.git
[semantic-release] › ✔  Allowed to push to the Git repository
[semantic-release] › ℹ  Start step "verifyConditions" of plugin "@semantic-release/changelog"
[semantic-release] › ✔  Completed step "verifyConditions" of plugin "@semantic-release/changelog"
[semantic-release] › ℹ  Start step "verifyConditions" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Completed step "verifyConditions" of plugin "@semantic-release/exec"
[semantic-release] › ℹ  Start step "verifyConditions" of plugin "@semantic-release/git"
[semantic-release] › ✔  Completed step "verifyConditions" of plugin "@semantic-release/git"
[semantic-release] › ℹ  Start step "verifyConditions" of plugin "@semantic-release/gitlab"
[semantic-release] [@semantic-release/gitlab] › ℹ  Verify GitLab authentication (https://gitlab.com/api/v4)
[semantic-release] › ✔  Completed step "verifyConditions" of plugin "@semantic-release/gitlab"
[semantic-release] › ℹ  Found git tag 1.0.3 associated with version 1.0.3 on branch main
[semantic-release] › ℹ  Found 1 commits since last release
[semantic-release] › ℹ  Start step "analyzeCommits" of plugin "@semantic-release/commit-analyzer"
[semantic-release] [@semantic-release/commit-analyzer] › ℹ  Analyzing commit: fix: testing
[semantic-release] [@semantic-release/commit-analyzer] › ℹ  The release type for the commit is patch
[semantic-release] [@semantic-release/commit-analyzer] › ℹ  Analysis of 1 commits complete: patch release
[semantic-release] › ✔  Completed step "analyzeCommits" of plugin "@semantic-release/commit-analyzer"
[semantic-release] › ℹ  Start step "analyzeCommits" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Completed step "analyzeCommits" of plugin "@semantic-release/exec"
[semantic-release] › ℹ  The next release version is 1.0.4
[semantic-release] › ℹ  Start step "verifyRelease" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Completed step "verifyRelease" of plugin "@semantic-release/exec"
[semantic-release] › ℹ  Start step "generateNotes" of plugin "@semantic-release/release-notes-generator"
[semantic-release] › ✔  Completed step "generateNotes" of plugin "@semantic-release/release-notes-generator"
[semantic-release] › ℹ  Start step "generateNotes" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Completed step "generateNotes" of plugin "@semantic-release/exec"
[semantic-release] › ℹ  Start step "prepare" of plugin "@semantic-release/changelog"
[semantic-release] [@semantic-release/changelog] › ℹ  Update /builds/hackerstack/semantic-release-demo/CHANGELOG.md
[semantic-release] › ✔  Completed step "prepare" of plugin "@semantic-release/changelog"
[semantic-release] › ℹ  Start step "prepare" of plugin "@semantic-release/exec"
[semantic-release] [@semantic-release/exec] › ℹ  Call script yq e '.version = "1.0.4"' -i Chart.yaml && yq e '.appVersion = "1.0.4"' -i Chart.yaml
[semantic-release] › ✔  Completed step "prepare" of plugin "@semantic-release/exec"
[semantic-release] › ℹ  Start step "prepare" of plugin "@semantic-release/git"
[semantic-release] [@semantic-release/git] › ℹ  Found 2 file(s) to commit
[semantic-release] [@semantic-release/git] › ℹ  Prepared Git release: 1.0.4
[semantic-release] › ✔  Completed step "prepare" of plugin "@semantic-release/git"
[semantic-release] › ℹ  Start step "generateNotes" of plugin "@semantic-release/release-notes-generator"
[semantic-release] › ✔  Completed step "generateNotes" of plugin "@semantic-release/release-notes-generator"
[semantic-release] › ℹ  Start step "generateNotes" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Completed step "generateNotes" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Created tag 1.0.4
[semantic-release] › ℹ  Start step "publish" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Completed step "publish" of plugin "@semantic-release/exec"
[semantic-release] › ℹ  Start step "publish" of plugin "@semantic-release/gitlab"
[semantic-release] [@semantic-release/gitlab] › ℹ  Published GitLab release: 1.0.4
[semantic-release] › ✔  Completed step "publish" of plugin "@semantic-release/gitlab"
[semantic-release] › ℹ  Start step "success" of plugin "@semantic-release/exec"
[semantic-release] › ✔  Completed step "success" of plugin "@semantic-release/exec"
[semantic-release] › ℹ  Start step "success" of plugin "@semantic-release/gitlab"
[semantic-release] › ✔  Completed step "success" of plugin "@semantic-release/gitlab"
[semantic-release] › ✔  Published release 1.0.4 on default channel

Grâce à ce système, je peux me concentrer pleinement sur mes développements. Fini les problèmes liés aux numéros de version et aux mises à jour manuelles… gain de temps et d'énergie… les mises à jour de version, les publications et les journaux de modifications sont automatisés, cohérents et reflètent les changements réels.

Merci de votre lecture, continuez à apprendre et à bientôt pour un prochain article 🚀