Comment déclencher une compilation uniquement si des modifications se produisent sur un ensemble particulier de fichiers

87

Comment dire à Jenkins / Hudson de déclencher une compilation uniquement pour les changements sur un projet particulier dans mon arbre Git?

xavier.seignard
la source

Réponses:

65

Le plugin Git a une option (région exclue) pour utiliser des expressions régulières pour déterminer s'il faut ignorer la construction en fonction du fait que les fichiers du commit correspondent à l'expression régulière de la région exclue.

Malheureusement, le plugin Git stock n'a pas de fonctionnalité "région incluse" pour le moment (1.15). Cependant, quelqu'un a publié des correctifs sur GitHub qui fonctionnent sur Jenkins et Hudson et qui implémentent la fonctionnalité souhaitée.

C'est un peu de travail à construire, mais cela fonctionne comme annoncé et a été extrêmement utile car l'un de mes arbres Git a plusieurs projets indépendants.

https://github.com/jenkinsci/git-plugin/pull/49

Mise à jour: Le plugin Git (1.16) a maintenant la fonction de région «incluse».

Aaron Kushner
la source
5
1.1.16 est le numéro de version correct pour la fonctionnalité incluse. (il n'y a pas de 1.16)
dan carter
Je ne peux pas le faire fonctionner, j'ai un dépôt avec plusieurs modules (domaine, commun, api, desktop_app, ...) Je veux déclencher une build pour desktop_app par exemple, je mets sur les "régions incluses" production_app / *, J'ai essayé plusieurs combinaisons comme ./desktop_app même chemin absolu. Et j'ai toujours eu Ignored commit c6e2b1dca0d1885: No paths matched included region whitelist. Un indice? Plus de détails ici: stackoverflow.com/questions/47439042/…
FranAguiar
38

En gros, vous avez besoin de deux emplois. Un pour vérifier si les fichiers ont changé et un pour faire la construction réelle:

Emploi n ° 1

Cela devrait être déclenché lors des changements dans votre référentiel Git. Il teste ensuite si le chemin que vous spécifiez ("src" ici) a changé, puis utilise la CLI de Jenkins pour déclencher un deuxième travail.

export JENKINS_CLI="java -jar /var/run/jenkins/war/WEB-INF/jenkins-cli.jar"
export JENKINS_URL=http://localhost:8080/
export GIT_REVISION=`git rev-parse HEAD`
export STATUSFILE=$WORKSPACE/status_$BUILD_ID.txt

# Figure out, whether "src" has changed in the last commit
git diff-tree --name-only HEAD | grep src

# Exit with success if it didn't
$? || exit 0

# Trigger second job
$JENKINS_CLI build job2 -p GIT_REVISION=$GIT_REVISION -s

Emploi n ° 2

Configurez ce travail pour prendre un paramètre GIT_REVISION comme ceci, pour vous assurer que vous construisez exactement la révision que le premier travail a choisi de construire.

Paramètre de chaîne de construction paramétré Vérification paramétrée de la construction Git

peritus
la source
6
Que faire si deux ou plusieurs validations se sont produites depuis la dernière génération? Je pense que vous risquez de manquer des changements dans src puisque vous n'examinez que le commit HEAD.
Adam Monsen
@AdamMonsen C'est vrai. Mais vous pouvez facilement adapter le script ci-dessus à n'importe quelle situation / condition sur laquelle vous voulez tester ... par exemple, pas de différence avec HEAD mais avec ce qui était HEAD la dernière fois que le script a été exécuté.
peritus
Il manque quelque chose à $? || exit 0... test $? -eq 0 || exit 0peut-être?
antak
31

Si vous utilisez une syntaxe déclarative de Jenkinsfile pour décrire votre pipeline de construction, vous pouvez utiliser la condition de changeset pour limiter l'exécution de l'étape uniquement au cas où des fichiers spécifiques sont modifiés. C'est maintenant une fonctionnalité standard de Jenkins et ne nécessite aucune configuration / logiciel supplémentaire.

stages {
    stage('Nginx') {
        when { changeset "nginx/*"}
        steps {
            sh "make build-nginx"
            sh "make start-nginx"
        }
    }
}

Vous pouvez combiner plusieurs conditions à l'aide de mots clés anyOfou allOfpour le comportement OR ou AND en conséquence:

when {
    anyOf {
        changeset "nginx/**"
        changeset "fluent-bit/**"
    }
}
steps {
    sh "make build-nginx"
    sh "make start-nginx"
}
Anton Golubev
la source
1
Gardez à l'esprit que cela ne fonctionne pas dans certains cas. Reportez-vous à issues.jenkins-ci.org/browse/JENKINS-26354 pour plus de détails.
tamerlaha
7

Bien que cela n'affecte pas les tâches uniques, vous pouvez utiliser ce script pour ignorer certaines étapes si la dernière validation ne contenait aucune modification:

/*
 * Check a folder if changed in the latest commit.
 * Returns true if changed, or false if no changes.
 */
def checkFolderForDiffs(path) {
    try {
        // git diff will return 1 for changes (failure) which is caught in catch, or
        // 0 meaning no changes 
        sh "git diff --quiet --exit-code HEAD~1..HEAD ${path}"
        return false
    } catch (err) {
        return true
    }
}

if ( checkFolderForDiffs('api/') ) {
    //API folder changed, run steps here
}
Au revoir StackExchange
la source
1
@Karl, n'hésitez pas à corriger le code si cela fonctionne pour vous. C'était le seul problème que j'avais lors de l'application de ce code (en cas d'échec de la construction, il ne réessayerait pas ce commit, si le dernier commit absolu ne changeait pas également le api/dossier.) Si vous pouvez résoudre ce problème, j'aimerais une modification suggérée !
Au revoir StackExchange le
2

Si la logique de choix des fichiers n'est pas triviale, je déclencherais l'exécution de script à chaque changement, puis j'écrirais un script pour vérifier si effectivement une construction est nécessaire, puis je déclencherais une construction si elle l'est.

Uri Cohen
la source
1

Vous pouvez utiliser Generic Webhook Trigger Plugin pour cela.

Avec une variable comme changed_fileset une expression$.commits[*].['modified','added','removed'][*] .

Vous pouvez avoir un texte de filtre comme $changed_fileset une expression régulière de filtre comme "folder/subfolder/[^"]+?"si folder/subfolderest le dossier qui devrait déclencher les compilations.

Tomas Bjerre
la source
J'essaye de faire ça mais je suis un peu perdu. comment envoyer le chemin du fichier modifié à jenkins? pouvez-vous expliquer un peu plus s'il vous plaît? Où mettre la variable changed_files?
Souad
Vous devez configurer un webhook dans le service Git que vous utilisez. S'il s'agit de GitHub, il y a un exemple ici: github.com/jenkinsci/generic-webhook-trigger-plugin/blob/master/…
Tomas Bjerre
En fait, lorsque j'utilise Bitbucket, j'ai réalisé que l'entrée Changed_files n'est pas disponible dans la charge utile de l'événement push dans bibucket (ref: confluence.atlassian.com/bitbucket/… ) donc je ne sais pas comment puis-je faire cela. Je vais me fier au message de validation, je pense. merci
Souad
1

J'ai répondu à cette question dans un autre post:

Comment obtenir la liste des fichiers modifiés depuis la dernière construction dans Jenkins / Hudson

#!/bin/bash

set -e

job_name="whatever"
JOB_URL="http://myserver:8080/job/${job_name}/"
FILTER_PATH="path/to/folder/to/monitor"

python_func="import json, sys
obj = json.loads(sys.stdin.read())
ch_list = obj['changeSet']['items']
_list = [ j['affectedPaths'] for j in ch_list ]
for outer in _list:
  for inner in outer:
    print inner
"

_affected_files=`curl --silent ${JOB_URL}${BUILD_NUMBER}'/api/json' | python -c "$python_func"`

if [ -z "`echo \"$_affected_files\" | grep \"${FILTER_PATH}\"`" ]; then
  echo "[INFO] no changes detected in ${FILTER_PATH}"
  exit 0
else
  echo "[INFO] changed files detected: "
  for a_file in `echo "$_affected_files" | grep "${FILTER_PATH}"`; do
    echo "    $a_file"
  done;
fi;

Vous pouvez ajouter la vérification directement en haut du shell d'exécution du travail, et ce sera le exit 0cas si aucun changement n'est détecté ... Par conséquent, vous pouvez toujours interroger le niveau supérieur pour que les check-in déclenchent une construction.

hhony
la source
1

J'ai écrit ce script pour ignorer ou exécuter des tests s'il y a des changements:

#!/bin/bash

set -e -o pipefail -u

paths=()
while [ "$1" != "--" ]; do
    paths+=( "$1" ); shift
done
shift

if git diff --quiet --exit-code "${BASE_BRANCH:-origin/master}"..HEAD ${paths[@]}; then
    echo "No changes in ${paths[@]}, skipping $@..." 1>&2
    exit 0
fi
echo "Changes found in ${paths[@]}, running $@..." 1>&2

exec "$@"

Vous pouvez donc faire quelque chose comme:

./scripts/git-run-if-changed.sh cmd vendor go.mod go.sum fixtures/ tools/ -- go test

user12513208
la source