Intégration continue «jolie» pour Python

116

C'est une question un peu… vaine, mais la sortie de BuildBot n'est pas particulièrement agréable à regarder.

Par exemple, par rapport à ..

..et d'autres, BuildBot a l' air plutôt .. archaïque

Je joue actuellement avec Hudson, mais il est très centré sur Java (bien qu'avec ce guide , je l'ai trouvé plus facile à configurer que BuildBot, et j'ai produit plus d'informations)

Fondamentalement: existe-t-il des systèmes d'intégration continue destinés à python, qui produisent beaucoup de graphiques brillants et autres?


Mise à jour: depuis ce temps, le projet Jenkins a remplacé Hudson en tant que version communautaire du package. Les auteurs originaux sont également passés à ce projet. Jenkins est maintenant un package standard sur Ubuntu / Debian, RedHat / Fedora / CentOS et autres. La mise à jour suivante est toujours essentiellement correcte. Le point de départ pour faire cela avec Jenkins est différent.

Mise à jour: après avoir essayé quelques alternatives, je pense que je vais rester avec Hudson. L'intégrité était agréable et simple, mais assez limitée. Je pense que Buildbot est mieux adapté pour avoir de nombreux esclaves de construction, plutôt que tout fonctionnant sur une seule machine comme je l'utilisais.

Configurer Hudson pour un projet Python était assez simple:

  • Téléchargez Hudson depuis http://hudson-ci.org/
  • Exécutez-le avec java -jar hudson.war
  • Ouvrez l'interface Web à l'adresse par défaut de http://localhost:8080
  • Accédez à Gérer Hudson, Plugins, cliquez sur "Mettre à jour" ou similaire
  • Installez le plugin Git (j'ai dû définir le gitchemin dans les préférences globales Hudson)
  • Créez un nouveau projet, entrez dans le référentiel, les intervalles d'interrogation SCM, etc.
  • Installer nosetestsvia easy_installsi ce n'est déjà fait
  • À l'étape de création, ajoutez nosetests --with-xunit --verbose
  • Cochez «Publish JUnit test result report» et définissez «Test report XMLs» sur **/nosetests.xml

C'est tout ce qu'il faut. Vous pouvez configurer des notifications par e-mail, et les plugins valent le coup d'œil. Quelques-uns que j'utilise actuellement pour les projets Python:

  • Plug-in SLOCCount pour compter les lignes de code (et les représenter graphiquement!) - vous devez installer sloccount séparément
  • Violations pour analyser la sortie PyLint (vous pouvez configurer des seuils d'avertissement, représenter graphiquement le nombre de violations sur chaque build)
  • Cobertura peut analyser la sortie coverage.py. Nosetest peut recueillir une couverture lors de l'exécution de vos tests, en utilisant nosetests --with-coverage(ceci écrit la sortie dans **/coverage.xml)
dbr
la source
Excellente question, je regarde des choses similaires en ce moment. Si vous optez pour une seule voie, pouvez-vous partager votre expérience avec le reste d'entre nous?
André
3
Je ne sais pas s'il était disponible lorsque vous avez écrit ceci: Utilisez le plugin Chuck Norris pour Hudson pour améliorer encore le contrôle de vos affaires!
Johannes Charra
8
Mise à jour pour 2011/2012 : Ceux qui envisagent Hudson devraient utiliser Jenkins , la suite open source du projet Hudson (Hudson est maintenant contrôlé par Oracle )
mindthief

Réponses:

41

Vous voudrez peut-être consulter Nose et le plugin de sortie Xunit . Vous pouvez lui faire exécuter vos tests unitaires et vos contrôles de couverture avec cette commande:

nosetests --with-xunit --enable-cover

Cela sera utile si vous souhaitez emprunter la route Jenkins ou si vous souhaitez utiliser un autre serveur CI prenant en charge les rapports de test JUnit.

De même, vous pouvez capturer la sortie de pylint en utilisant le plugin violations pour Jenkins

Jason Baker
la source
4
Nose inclut maintenant le plugin xunit par défaut -nosetests --with-xunit
dbr
3
Alors, comment exécuter l'audit de Pylint alors? Quand je nosetests --with-xunit --enable-auditreçoisnosetests: error: no such option: --enable-audit
Adam Parkin
2
Réponse modernisée, le contenu de NoseXUnit est maintenant intégré et renommé de malheureux quand il est descendu --with-nosexuniten --with-xunit.
dbr
10

Je ne sais pas si ça ferait l'affaire: Bitten est fait par les gars qui écrivent Trac et est intégré à Trac. Apache Gump est l'outil CI utilisé par Apache. Il est écrit en Python.

edomaur
la source
9

Nous avons eu beaucoup de succès avec TeamCity en tant que serveur CI et en utilisant nose comme testeur. Le plugin Teamcity pour nosetests vous donne le nombre de réussite / échec, un affichage lisible en cas d'échec du test (qui peut être envoyé par courrier électronique). Vous pouvez même voir les détails des échecs de test pendant l'exécution de la pile.

Si bien sûr prend en charge des choses comme l'exécution sur plusieurs machines, et il est beaucoup plus simple à configurer et à maintenir que buildbot.

Kozyarchuk
la source
6

Le bambou d'Atlassian vaut également le détour. Toute la suite Atlassian (JIRA, Confluence, FishEye, etc.) est plutôt douce.

Russ
la source
6

Je suppose que ce fil est assez ancien, mais voici mon point de vue avec hudson:

J'ai décidé d'aller avec pip et de mettre en place un repo (pénible à travailler mais joli eggbasket), sur lequel Hudson télécharge automatiquement avec des tests réussis. Voici mon script approximatif à utiliser avec un script d'exécution de configuration hudson comme: /var/lib/hudson/venv/main/bin/hudson_script.py -w $ WORKSPACE -p mon.package -v $ BUILD_NUMBER, il suffit de le mettre ** / coverage.xml, pylint.txt et nosetests.xml dans les bits de configuration:

#!/var/lib/hudson/venv/main/bin/python
import os
import re
import subprocess
import logging
import optparse

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(levelname)s %(message)s')

#venvDir = "/var/lib/hudson/venv/main/bin/"

UPLOAD_REPO = "http://ldndev01:3442"

def call_command(command, cwd, ignore_error_code=False):
    try:
        logging.info("Running: %s" % command)
        status = subprocess.call(command, cwd=cwd, shell=True)
        if not ignore_error_code and status != 0:
            raise Exception("Last command failed")

        return status

    except:
        logging.exception("Could not run command %s" % command)
        raise

def main():
    usage = "usage: %prog [options]"
    parser = optparse.OptionParser(usage)
    parser.add_option("-w", "--workspace", dest="workspace",
                      help="workspace folder for the job")
    parser.add_option("-p", "--package", dest="package",
                      help="the package name i.e., back_office.reconciler")
    parser.add_option("-v", "--build_number", dest="build_number",
                      help="the build number, which will get put at the end of the package version")
    options, args = parser.parse_args()

    if not options.workspace or not options.package:
        raise Exception("Need both args, do --help for info")

    venvDir = options.package + "_venv/"

    #find out if venv is there
    if not os.path.exists(venvDir):
        #make it
        call_command("virtualenv %s --no-site-packages" % venvDir,
                     options.workspace)

    #install the venv/make sure its there plus install the local package
    call_command("%sbin/pip install -e ./ --extra-index %s" % (venvDir, UPLOAD_REPO),
                 options.workspace)

    #make sure pylint, nose and coverage are installed
    call_command("%sbin/pip install nose pylint coverage epydoc" % venvDir,
                 options.workspace)

    #make sure we have an __init__.py
    #this shouldn't be needed if the packages are set up correctly
    #modules = options.package.split(".")
    #if len(modules) > 1: 
    #    call_command("touch '%s/__init__.py'" % modules[0], 
    #                 options.workspace)
    #do the nosetests
    test_status = call_command("%sbin/nosetests %s --with-xunit --with-coverage --cover-package %s --cover-erase" % (venvDir,
                                                                                     options.package.replace(".", "/"),
                                                                                     options.package),
                 options.workspace, True)
    #produce coverage report -i for ignore weird missing file errors
    call_command("%sbin/coverage xml -i" % venvDir,
                 options.workspace)
    #move it so that the code coverage plugin can find it
    call_command("mv coverage.xml %s" % (options.package.replace(".", "/")),
                 options.workspace)
    #run pylint
    call_command("%sbin/pylint --rcfile ~/pylint.rc -f parseable %s > pylint.txt" % (venvDir, 
                                                                                     options.package),
                 options.workspace, True)

    #remove old dists so we only have the newest at the end
    call_command("rm -rfv %s" % (options.workspace + "/dist"),
                 options.workspace)

    #if the build passes upload the result to the egg_basket
    if test_status == 0:
        logging.info("Success - uploading egg")
        upload_bit = "upload -r %s/upload" % UPLOAD_REPO
    else:
        logging.info("Failure - not uploading egg")
        upload_bit = ""

    #create egg
    call_command("%sbin/python setup.py egg_info --tag-build=.0.%s --tag-svn-revision --tag-date sdist %s" % (venvDir,
                                                                                                              options.build_number,
                                                                                                              upload_bit),
                 options.workspace)

    call_command("%sbin/epydoc --html --graph all %s" % (venvDir, options.package),
                 options.workspace)

    logging.info("Complete")

if __name__ == "__main__":
    main()

Quand il s'agit de déployer des éléments, vous pouvez faire quelque chose comme:

pip -E /location/of/my/venv/ install my_package==X.Y.Z --extra-index http://my_repo

Et puis les gens peuvent développer des choses en utilisant:

pip -E /location/of/my/venv/ install -e ./ --extra-index http://my_repo

Ce truc suppose que vous avez une structure de dépôt par paquet avec un setup.py et des dépendances toutes configurées, alors vous pouvez simplement vérifier le tronc et exécuter ce truc dessus.

J'espère que ça aide quelqu'un.

------mettre à jour---------

J'ai ajouté epydoc qui s'intègre très bien avec hudson. Ajoutez simplement javadoc à votre configuration avec le dossier html

Notez que pip ne prend pas en charge correctement le drapeau -E ces jours-ci, vous devez donc créer votre venv séparément

Nick Holden
la source
Cette réponse est très utile et contient beaucoup de détails sur les composants internes de Python CI, quelque chose que vous n'obtiendrez pas gratuitement de Jenkins ou autre. Merci!
maksimov
3

Si vous envisagez une solution CI hébergée et que vous faites de l'open source, vous devriez également vous pencher sur Travis CI - il a une très bonne intégration avec GitHub. Bien que cela ait commencé comme un outil Ruby, ils ont ajouté le support Python il y a quelque temps.

Alex Dupuy
la source
2

Le signal est une autre option. Vous pouvez en savoir plus et regarder une vidéo également ici .

Diego Carrion
la source
2

Je considérerais CircleCi - il a un excellent support Python et une très belle sortie.

Paul Biggar
la source
1

binstar de continuum est maintenant capable de déclencher des builds à partir de github et peut compiler pour linux, osx et windows ( 32/64 ). ce qui est bien, c'est qu'il vous permet vraiment de coupler étroitement distribution et intégration continue. C'est traverser les t et parsemer les I de l'intégration. Le site, flux de travail et les outils sont vraiment poli et Conda est AFAIK la façon la plus robuste et pythonique à la distribution de modules Python complexes, où vous avez besoin pour envelopper et distribuer les bibliothèques C / C ++ / Fotran.

Jelle
la source
0

Nous avons utilisé un peu mordu. Il est joli et s'intègre bien avec Trac, mais c'est une douleur dans le cul à personnaliser si vous avez un flux de travail non standard. De plus, il n'y a tout simplement pas autant de plugins que pour les outils les plus populaires. Actuellement, nous évaluons Hudson en remplacement.

Allen
la source
0

Vérifiez rultor.com . Comme cet article l' explique, il utilise Docker pour chaque build. Grâce à cela, vous pouvez configurer ce que vous voulez dans votre image Docker, y compris Python.

yegor256
la source
0

Petit avertissement, j'ai en fait dû créer une solution comme celle-ci pour un client qui voulait un moyen de tester et de déployer automatiquement tout code sur un push git et de gérer les tickets de problème via des notes git. Cela a également conduit à mon travail sur le projet AIMS .

On pourrait facilement juste installer un système de nœud nu qui a un utilisateur de construire et de gérer leur construction par make(1), expect(1), crontab(1)/ systemd.unit(5)etincrontab(1) . On pourrait même aller plus loin et utiliser ansible et céleri pour les versions distribuées avec un magasin de fichiers gridfs / nfs.

Cependant, je ne m'attendrais pas à ce que quelqu'un d'autre qu'un gars d'UNIX Graybeard ou un ingénieur / architecte de niveau Principle aille aussi loin. C'est juste une bonne idée et une expérience d'apprentissage potentielle car un serveur de build n'est rien de plus qu'un moyen d'exécuter arbitrairement des tâches scriptées de manière automatisée.

Dwight Spencer
la source