web-dev-qa-db-fra.com

Créer un référentiel Gradle pour le développement hors ligne

Je travaille sur l'implémentation d'un système de construction Gradle pour un logiciel qui a des parties développées dans une zone sans connectivité Internet ou la possibilité d'installer un serveur Maven/Ivy (comme Nexus). Pour soutenir le développement dans ces environnements, je mets en place un plugin Gradle qui permet la génération d'un "espace de travail hors ligne".

J'ai à l'origine implémenté cette fonctionnalité en déclenchant la résolution de chaque configuration dans le projet (déclenchant le téléchargement de toutes les dépendances), puis parcourant l'arborescence de dépendance entière de chaque configuration et copiant la copie locale en cache de la dépendance dans l'espace de travail hors ligne. (Une tâche de copie a été générée pour chaque opération de copie.) Ces fichiers JAR seraient ensuite référencés à l'aide d'un référentiel flatDir.

Cette implémentation a effectué son travail à l'aide d'un bloc afterEvaluate. Bien que cela ait bien fonctionné dans Gradle 2.0, il déclenche un avertissement de dépréciation dans Gradle 2.2.1 car le déclenchement de la résolution est en quelque sorte considéré comme modifiant une configuration une fois qu'elle a déjà été résolue (Attempting to change configuration ':core:runtime' after it has been included in dependency resolution. This behaviour has been deprecated and is scheduled to be removed in Gradle 3.0). Dans l'ensemble, cette approche semble plutôt hacky car elle nécessite également que je modifie les fichiers build.gradle pour répertorier explicitement toutes les dépendances transitives car aucun fichier POM n'est disponible pour spécifier correctement les dépendances.

Une approche plus élégante semble créer un référentiel Maven local de toutes les dépendances (y compris les fichiers POM, les fichiers JAR source, les fichiers JAR javadoc, etc.), puis utiliser simplement le type de référentiel mavenLocal (). Malheureusement, je ne suis pas sûr de savoir comment procéder correctement lorsque je n'ai pas besoin de déclencher la résolution d'artefact pour effectuer cette opération.

Existe-t-il un meilleur moyen de réaliser le téléchargement complet des artefacts d'une manière facile à empaqueter que de simplement compresser l'intégralité de mon répertoire $ USER_HOME/.gradle?

18
Chris Lieb

Pour avoir une version hors ligne, vous devez en quelque sorte fournir toutes les dépendances requises. L'une des options ici consiste simplement à valider ces fichiers JAR dans le contrôle de version. La partie difficile est de collecter toutes ces dépendances. Pour cela, il est possible d'avoir un fichier build.gradle qui peut fonctionner en deux modes (en ligne et hors ligne):

buildscript {
    repositories {
        if ('allow' == System.properties['build.network_access']) {
            mavenCentral()
        } else {
            maven {
                url 'dependencies'
            }
        }
    }
    dependencies {
        classpath 'com.Android.tools.build:gradle:1.2.0-beta2'
    }
}

Pour exécuter en mode hors ligne, tapez:

gradle --offline

Et pour fonctionner en mode en ligne:

gradle -Dbuild.network_access=allow

Et pour collecter toutes les dépendances, utilisez ce script qui exécutera gradle en mode en ligne, récupérez les dépendances à mettre en cache dans ${project_dir}/.gradle_home et copiez les artefacts dans le référentiel maven local dans le dossier dependencies.

#!/usr/bin/python

import sys
import os
import subprocess
import glob
import shutil

# Place this in build.gradle:
# repositories {
#     if ('allow' == System.properties['build.network_access']) {
#         mavenCentral()
#     } else {
#         maven { url 'dependencies' }
#     }
# }
def main(argv):
    project_dir = os.path.dirname(os.path.realpath(__file__))
    repo_dir = os.path.join(project_dir, "dependencies")
    temp_home = os.path.join(project_dir, ".gradle_home")
    if not os.path.isdir(temp_home):
        os.makedirs(temp_home)
    subprocess.call(["gradle", "-g", temp_home, "-Dbuild.network_access=allow"])
    cache_files = os.path.join(temp_home, "caches/modules-*/files-*")
    for cache_dir in glob.glob(cache_files):
        for cache_group_id in os.listdir(cache_dir):
            cache_group_dir = os.path.join(cache_dir, cache_group_id)
            repo_group_dir = os.path.join(repo_dir, cache_group_id.replace('.', '/'))
            for cache_artifact_id in os.listdir(cache_group_dir):
                cache_artifact_dir = os.path.join(cache_group_dir, cache_artifact_id)
                repo_artifact_dir = os.path.join(repo_group_dir, cache_artifact_id)
                for cache_version_id in os.listdir(cache_artifact_dir):
                    cache_version_dir = os.path.join(cache_artifact_dir, cache_version_id)
                    repo_version_dir = os.path.join(repo_artifact_dir, cache_version_id)
                    if not os.path.isdir(repo_version_dir):
                        os.makedirs(repo_version_dir)
                    cache_items = os.path.join(cache_version_dir, "*/*")
                    for cache_item in glob.glob(cache_items):
                        cache_item_name = os.path.basename(cache_item)
                        repo_item_path = os.path.join(repo_version_dir, cache_item_name)
                        print "%s:%s:%s (%s)" % (cache_group_id, cache_artifact_id, cache_version_id, cache_item_name)
                        shutil.copyfile(cache_item, repo_item_path)
    shutil.rmtree(temp_home)
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv))

Ainsi, après chaque changement de dépendance, exécutez simplement ce script et validez les modifications dans le dossier dependencies. Ensuite, vous pouvez créer hors ligne avec gradle --offline ou simplement gradle.

13
wonder.mice

Dans le passé, j'utilisais une solution similaire, bien que "mon" script de copie ait été fait en groovy au lieu de python.

J'ai détecté une autre approche il y a quelques semaines: il y a le plugin ivypot . Maintenant, vous n'avez plus besoin d'utiliser de script "externe", le plugin est capable de copier toutes les dépendances dans un répertoire local qui est un dépôt ivy.

Un projet de test peut être trouvé sur github . Je peux fournir un anglais README en cas de demande).

4
user959232

La solution la plus simple consiste à prendre un instantané de l'ensemble du répertoire du cache de dépendances: ~/.gradle

Il y a cependant quelques défis:

  1. Cela prendra une quantité importante d'espace disque
  2. Il contiendra des milliers de fichiers (difficiles à manipuler manuellement)
  3. Comme il s'agit d'un cache et non d'un référentiel , Gradle est sans obligation de garder les choses en permanence

Pour résoudre les points 2 et 3 ci-dessus, je suggère d'utiliser Git (et également des copies de sauvegarde) pour vous aider à vaincre le démon de nettoyage du cache de Gradle et éviter l'erreur: " pas de version en cache disponible pour hors ligne mode ". Lorsque vous disposez d'un cache de dépendances entièrement rempli (--offline les builds fonctionnent), validez le cache en contrôle de version afin de pouvoir le restaurer plus tard si nécessaire (en utilisant git stash pour ignorer toutes les modifications, par exemple).


Le ~/.gradle devrait toujours fonctionner comme un lien symbolique vers un autre endroit de votre système de fichiers si cela simplifie la gestion de projet ou les sauvegardes. Il peut également y avoir une certaine amélioration de ce schéma disponible en utilisant un --project-cache-dir , mais je n'ai pas encore essayé.


Cela implique une gestion manuelle du référentiel, car La gestion du cache Gradle travaille activement contre l'objectif d'un développement hors ligne stable - mais cela devrait vous aider à continuer à fonctionner.

0
nobar