Introduction
Gérer les versions de ses charts Helm peut rapidement devenir complexe. Les outils classiques d'automatisation de releases imposent souvent de créer un dépôt dédié par chart, ce qui devient difficile à maintenir lorsque leur nombre augmente.
Nx est une solution élégante pour simplifier cette gestion au sein d'un monorepo.
Cette publication s'inspire des travaux d'Antoine Caron. Je vous invite à lire son article Managing Terraform Modules with Nx Monorepo.
Qu'est-ce que Nx ?
Nx est un outil de build conçu pour gérer des monorepos. À l'origine orienté vers les technologies web, il est compatible avec tous les langages et permet d'orchestrer des tâches (lint, build, test, release) sur des projets indépendants au sein d'un même dépôt.
Configuration d'un environnement Nx pour Helm
Construisons ensemble la structure d'un monorepo de charts Helm.
Création du workspace Nx
npx create-nx-workspace@latest helm-charts --preset=npm
cd helm-chartsArborescence
- helm-charts
- charts
- example-chart
- templates
- Chart.yaml
- package.json
- nx.json
- package.json
Chaque chart Helm dispose d'un dossier dédié sous charts/ et d'un fichier package.json permettant de configurer Nx.
Configuration du monorepo
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"release": {
"projects": ["charts/*"],
"projectsRelationship": "independent",
"releaseTagPattern": "{projectName}@v{version}",
"version": {
"conventionalCommits": true
},
"changelog": {
"projectChangelogs": {
"createRelease": "github",
"file": "{projectRoot}/CHANGELOG.md",
"renderOptions": {
"authors": false,
"versionTitleTemplate": "## {version} ({date})",
"commitReferences": true,
"versionTitleDate": true
}
}
},
"git": {
"commit": true,
"tag": true,
"commitMessage": "chore(release): {projectName} {version}"
}
}
}Quelques points clés :
projectsRelationship: "independent"— chaque chart a sa propre versionreleaseTagPattern— les tags seront formatés en[email protected]conventionalCommits— la version est calculée automatiquement à partir des messages de commit
Configuration par chart
Chaque chart dispose d'un package.json qui décrit ses targets Nx :
{
"name": "example-chart",
"version": "0.1.0",
"scripts": {
"post-release": "nx run example-chart:sync-chart-version"
},
"nx": {
"targets": {
"lint": {
"executor": "nx:run-commands",
"options": {
"command": "helm lint --strict .",
"cwd": "charts/example-chart"
}
},
"sync-chart-version": {
"executor": "nx:run-commands",
"options": {
"command": "node ../../scripts/sync-chart-version.mjs example-chart",
"cwd": "charts/example-chart"
}
},
"package": {
"executor": "@nx-extensions/helm:package",
"options": {
"chartFolder": "charts/example-chart",
"outputFolder": "{workspaceRoot}/dist/charts/{projectRoot}",
"remote": "oci://localhost:5000/helm-charts"
}
}
}
}
}On définit ici trois targets :
lint— valide le lint du chart avec la commandehelm lintsync-chart-version— synchronise la version dupackage.jsonNx vers le fichierChart.yamlpackage— empaquette et publie le chart via l'extension @nx-extensions/helm
Script de synchronisation de version
Le script sync-chart-version.mjs lit la version depuis le package.json du projet Nx et l'injecte dans le Chart.yaml :
import { readFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const chartName = process.argv[2];
const chartDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'charts', chartName);
const { version } = JSON.parse(readFileSync(join(chartDir, 'package.json'), 'utf8'));
const chartPath = join(chartDir, 'Chart.yaml');
const chart = readFileSync(chartPath, 'utf8');
writeFileSync(chartPath, chart.replace(/^version: .+$/m, `version: ${version}`));Votre monorepo est prêt à accueillir vos charts Helm.
Le flux de release
Voici comment s'enchaînent les étapes lors d'un release :
- Un développeur pousse un commit sur la branche principale
- Nx détecte les projets impactés par le commit
- Pour chaque chart modifié,
nx releaseexécute :- Calcul de la nouvelle version (conventional commits)
- Mise à jour du
CHANGELOG.md - Création du tag Git (
[email protected]) - Exécution du hook
post-releasequi appellesync-chart-versionpuispackage
- Le chart est publié dans un registry OCI
Déclenchement manuel
# Release d'un chart spécifique
nx release example-chart --skip-publish
# Release de tous les charts modifiés
nx release --projects=charts/*Vous pouvez faire un test à blanc en ajoutant l'option
--dry-run
CI/CD avec GitHub Actions
name: Nx CI
on:
push:
branches: ["main"]
jobs:
lint-release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with:
version: 11
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run lint
run: npx nx affected -t lint
- name: Run Nx release
if: github.ref == 'refs/heads/main'
run: CI=true npx nx release --skip-publish
- name: Package and push Helm charts
if: github.ref == 'refs/heads/main'
run: npx nx run-many -t package --projects=charts/*
- name: Chart post-release tasks
if: github.ref == 'refs/heads/main'
run: |
npx nx run-many -t post-release --projects=charts/*
git add charts/
# if there are changes to commit, commit and push them
if [ -n "$(git status --porcelain)" ]; then
git commit -m "chore(post-release): sync chart version"
git push
else
echo "No changes detected. Skipping commit."
fiConclusion
Nx permet de centraliser et d'automatiser le versionnage de vos charts Helm dans un monorepo, sans duplication d'outillage. Chaque chart garde sa propre version, les changelogs sont générés automatiquement, et la publication dans un registry OCI s'intègre nativement dans votre pipeline CI/CD.
Cette approche s'adapte aussi à d'autres types de projets (modules Terraform, packages npm, etc.) — une fois le pattern maîtrisé, il se décline facilement.