Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

Cet article est destiné aux développeurs Java qui ont besoin de publier rapidement leurs produits dans les référentiels centraux sonatype et/ou maven à l'aide de GitLab. Dans cet article, je parlerai de la configuration de gitlab-runner, gitlab-ci et maven-plugin pour résoudre ce problème.

Conditions préalables:

  • Stockage sécurisé des clés mvn et GPG.
  • Exécution sécurisée des tâches publiques de CI.
  • Téléchargement d'artefacts (version/instantané) vers des référentiels publics.
  • Vérification automatique des versions pour publication dans Maven Central.
  • Une solution générale pour télécharger des artefacts vers un référentiel pour plusieurs projets.
  • Simplicité et facilité d'utilisation.

Teneur

Informations générales

  • Une description détaillée du mécanisme de publication d'artefacts dans Maven Central via le service d'hébergement de référentiel Sonatype OSS a déjà été décrite dans cet article par utilisateur Googleplex, je ferai donc référence à cet article aux bons endroits.
  • Pré-inscrivez-vous pour Sonatype JIRA et ouvrez un ticket pour ouvrir le dépôt (lisez la section pour plus de détails Créer un ticket sur Sonatype JIRA). Après avoir ouvert le référentiel, la paire login/mot de passe de JIRA (ci-après dénommé le compte Sonatype) sera utilisée pour télécharger des artefacts sur Sonatype nexus.
  • Ensuite, le processus de génération d’une clé GPG est décrit très sèchement. Voir la section pour plus de détails Configurer GnuPG pour signer des artefacts
  • Si vous utilisez la console Linux pour générer une clé GPG (gnupg/gnupg2), alors vous devez installer outils-rng pour générer de l'entropie. Sinon, la génération des clés peut prendre beaucoup de temps.
  • Prestations de stockage public Clés GPG

Au contenu

Configurer un projet de déploiement dans GitLab

  • Tout d'abord, vous devez créer et configurer un projet dans lequel le pipeline sera stocké pour déployer les artefacts. J'ai nommé mon projet simplement et sans complication - déployer
  • Après avoir créé le référentiel, vous devez restreindre l'accès pour modifier le référentiel.
    Accédez au projet -> Paramètres -> Référentiel -> Branches protégées. Nous supprimons toutes les règles et ajoutons une seule règle avec Wildcard * avec le droit de pousser et de fusionner uniquement pour les utilisateurs ayant le rôle de mainteneur. Cette règle fonctionnera pour tous les utilisateurs de ce projet et du groupe auquel ce projet appartient.
    Configuration de GitLab CI pour télécharger un projet Java sur Maven Central
  • S'il y a plusieurs responsables, alors la meilleure solution serait en principe de limiter l'accès au projet.
    Accédez au projet -> Paramètres -> Général -> Visibilité, fonctionnalités du projet, autorisations et définissez la visibilité du projet sur Chef.
    J'ai un projet accessible au public, puisque j'utilise mon propre GitLab Runner et que moi seul ai accès pour modifier le référentiel. Eh bien, en fait, ce n’est pas dans mon intérêt d’afficher des informations privées dans les journaux publics des pipelines.
  • Renforcer les règles de changement de référentiel
    Accédez au projet -> Paramètres -> Référentiel -> Règles Push et définissez la restriction Committer, vérifiez si l'auteur est un utilisateur GitLab. Je recommande également de configurer valider la signatureet définissez l'indicateur Rejeter les validations non signées.
  • Ensuite, vous devez configurer un déclencheur pour lancer des tâches
    Accédez au projet -> Paramètres -> CI / CD -> Déclencheurs de pipeline et créez un nouveau jeton de déclenchement
    Ce token peut être immédiatement ajouté à la configuration générale des variables d'un groupe de projets.
    Allez dans le groupe -> Paramètres -> CI / CD -> Variables et ajoutez une variable DEPLOY_TOKEN avec jeton de déclenchement en valeur.

Au contenu

Exécuteur GitLab

Cette section décrit la configuration pour exécuter des tâches lors du déploiement à l'aide de votre propre exécuteur (spécifique) et public (partagé).

Coureur spécifique

J'utilise mes propres patins parce que, tout d'abord, c'est pratique, rapide et bon marché.
Pour un coureur, je recommande un VDS Linux avec 1 CPU, 2 Go de RAM, 20 Go de disque dur. Le prix d'émission est de ~3000₽ par an.

Mon coureur

Pour le coureur, j'ai pris un processeur VDS 4, 4 Go de RAM, 50 Go de SSD. J'ai coûté ~11000 XNUMX₽ et je ne l'ai jamais regretté.
J'ai un total de 7 machines. 5 sur Aruba et 2 sur Ihor.

Nous avons donc un coureur. Nous allons maintenant le configurer.
Nous allons sur la machine via SSH et installons Java, git, maven, gnupg2.

Au contenu

Installer Gitlab Runner

  • Créer un nouveau groupe runner
    sudo groupadd runner
  • Créez un répertoire pour le cache maven et attribuez des autorisations de groupe runner
    Vous pouvez ignorer ce point si vous ne prévoyez pas d'exécuter plusieurs exécuteurs sur une seule machine.

    mkdir -p /usr/cache/.m2/repository
    chown -R :runner /usr/cache
    chmod -R 770 /usr/cache
  • Créer un utilisateur gitlab-deployer et ajouter au groupe runner
    useradd -m -d /home/gitlab-deployer gitlab-deployer
    usermod -a -G runner gitlab-deployer
  • Ajouter au fichier /etc/ssh/sshd_config ligne suivante
    AllowUsers root@* [email protected]
  • Redémarrer sshd
    systemctl restart sshd
  • Définition d'un mot de passe pour l'utilisateur gitlab-deployer (peut être simple, puisqu'il y a une restriction pour localhost)
    passwd gitlab-deployer
  • Installer GitLab Runner (Linux x86-64)
    sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
    sudo chmod +x /usr/local/bin/gitlab-runner
    ln -s /usr/local/bin/gitlab-runner /etc/alternatives/gitlab-runner
    ln -s /etc/alternatives/gitlab-runner /usr/bin/gitlab-runner
  • Accédez au site Web gitlab.com -> déployer-project -> Paramètres -> CI/CD -> Runners -> Specific Runners et copiez le jeton d'enregistrement

Écran

Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

  • Inscrire un coureur
    gitlab-runner register --config /etc/gitlab-runner/gitlab-deployer-config.toml

Processus

Runtime platform arch=amd64 os=linux pid=17594 revision=3001a600 version=11.10.0
Running in system-mode.
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://gitlab.com/
Please enter the gitlab-ci token for this runner:
REGISTRATION_TOKEN
Please enter the gitlab-ci description for this runner:
[ih1174328.vds.myihor.ru]: Deploy Runner
Please enter the gitlab-ci tags for this runner (comma separated):
deploy
Registering runner... succeeded                     runner=ZvKdjJhx
Please enter the executor: docker-ssh, parallels, virtualbox, docker-ssh+machine, kubernetes, docker, ssh, docker+machine, shell:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

  • Nous vérifions que le coureur est inscrit. Allez sur le site gitlab.com -> déployer-project -> Paramètres -> CI/CD -> Runners -> Specific Runners -> Runners activés pour ce projet

Écran

Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

  • Ajouter séparé service /etc/systemd/system/gitlab-deployer.service
    [Unit]
    Description=GitLab Deploy Runner
    After=syslog.target network.target
    ConditionFileIsExecutable=/usr/local/bin/gitlab-runner
    [Service]
    StartLimitInterval=5
    StartLimitBurst=10
    ExecStart=/usr/local/bin/gitlab-runner "run" "--working-directory" "/home/gitlab-deployer" "--config" "/etc/gitlab-runner/gitlab-deployer-config.toml" "--service" "gitlab-deployer" "--syslog" "--user" "gitlab-deployer"
    Restart=always
    RestartSec=120
    [Install]
    WantedBy=multi-user.target
  • Commençons le service.
    systemctl enable gitlab-deployer.service
    systemctl start gitlab-deployer.service
    systemctl status gitlab-deployer.service
  • On vérifie que le coureur court.

Exemple

Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

Au contenu

Générer des clés GPG

  • Depuis la même machine, nous nous connectons via ssh sous l'utilisateur gitlab-deployer (c'est important pour générer la clé GPG)

    ssh [email protected]

  • Nous générons une clé en répondant aux questions. J'ai utilisé mon propre nom et mon adresse e-mail.
    Assurez-vous de spécifier le mot de passe de la clé. Les artefacts seront signés avec cette clé.

    gpg --gen-key 

  • Chèque

    gpg --list-keys -a
    /home/gitlab-deployer/.gnupg/pubring.gpg
    ----------------------------------------
    pub   4096R/00000000 2019-04-19
    uid                  Petruha Petrov <[email protected]>
    sub   4096R/11111111 2019-04-19

  • Télécharger notre clé publique sur le serveur de clés

    gpg --keyserver keys.gnupg.net --send-key 00000000
    gpg: sending key 00000000 to hkp server keys.gnupg.net

Au contenu

Configurer Maven

  • Connectez-vous en tant qu'utilisateur gitlab-deployer
    su gitlab-deployer 
  • Créer un répertoire maven dépôt et lien vers le cache (ne vous y trompez pas)
    Vous pouvez ignorer ce point si vous ne prévoyez pas d'exécuter plusieurs exécuteurs sur une seule machine.

    mkdir -p ~/.m2/repository
    ln -s /usr/cache/.m2/repository /home/gitlab-deployer/.m2/repository
  • Créer une clé principale
    mvn --encrypt-master-password password
    {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}
  • Créez un fichier ~/.m2/settings-security.xml
    <settingsSecurity>
    <master>{hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}</master>
    </settingsSecurity>
  • Cryptage du mot de passe du compte Sonatype
    mvn --encrypt-password SONATYPE_PASSWORD
    {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}
  • Créez un fichier ~/.m2/settings.xml
    <settings>  
    <profiles>
        <profile>
            <id>env</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <gpg.passphrase>GPG_SECRET_KEY_PASSPHRASE</gpg.passphrase>
            </properties>
        </profile>
    </profiles>
    <servers>
        <server>
            <id>sonatype</id>
            <username>SONATYPE_USERNAME</username>
            <password>{98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}</password>
        </server>
    </servers>
    </settings>


GPG_SECRET_KEY_PASSPHRASE - mot de passe pour la clé GPG
SONATYPE_USERNAME — connexion au compte sonatype

Ceci termine la configuration du coureur, vous pouvez passer à la section CI GitLab

Au contenu

Coureur partagé

Générer des clés GPG

  • Tout d'abord, vous devez créer une clé GPG. Pour ce faire, installez gnupg.

    yum install -y gnupg

  • Nous générons une clé en répondant aux questions. J'ai utilisé mon propre nom et mon adresse e-mail. Assurez-vous de spécifier le mot de passe de la clé.

    gpg --gen-key 

  • Affichage des informations sur la clé

    gpg --list-keys -a
    pub   rsa3072 2019-04-24 [SC] [expires: 2021-04-23]
      2D0D1706366FC4AEF79669E24D09C55BBA3FD728
    uid           [ultimate] tttemp <[email protected]>
    sub   rsa3072 2019-04-24 [E] [expires: none]

  • Télécharger notre clé publique sur le serveur de clés

    gpg --keyserver keys.gnupg.net --send-key 2D0D1706366FC4AEF79669E24D09C55BBA3FD728
    gpg: sending key 2D0D1706366FC4AEF79669E24D09C55BBA3FD728 to hkp server keys.gnupg.net

  • Nous obtenons la clé privée

    gpg --export-secret-keys --armor 2D0D1706366FC4AEF79669E24D09C55BBA3FD728
    -----BEGIN PGP PRIVATE KEY BLOCK-----
    lQWGBFzAqp8BDADN41CPwJ/gQwiKEbyA902DKw/WSB1AvZQvV/ZFV77xGeG4K7k5
    ...
    =2Wd2
    -----END PGP PRIVATE KEY BLOCK-----

  • Accédez aux paramètres du projet -> Paramètres -> CI / CD -> Variables et enregistrez la clé privée dans une variable GPG_SECRET_KEY
    Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

Au contenu

Configurer Maven

  • Créer une clé principale
    mvn --encrypt-master-password password
    {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}
  • Accédez aux paramètres du projet -> Paramètres -> CI / CD -> Variables et enregistrez dans une variable SETTINGS_SECURITY_XML les lignes suivantes :
    <settingsSecurity>
    <master>{hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}</master>
    </settingsSecurity>
  • Cryptage du mot de passe du compte Sonatype
    mvn --encrypt-password SONATYPE_PASSWORD
    {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}
  • Accédez aux paramètres du projet -> Paramètres -> CI / CD -> Variables et enregistrez dans une variable SETTINGS_XML les lignes suivantes :
    <settings>  
    <profiles>
        <profile>
            <id>env</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <gpg.passphrase>GPG_SECRET_KEY_PASSPHRASE</gpg.passphrase>
            </properties>
        </profile>
    </profiles>
    <servers>
        <server>
            <id>sonatype</id>
            <username>sonatype_username</username>
            <password>{98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}</password>
        </server>
    </servers>
    </settings>


GPG_SECRET_KEY_PASSPHRASE - mot de passe pour la clé GPG
SONATYPE_USERNAME — connexion au compte sonatype

Au contenu

Déployer l'image Docker

  • Nous créons un Dockerfile assez simple pour exécuter des tâches de déploiement avec la version requise de Java. Vous trouverez ci-dessous un exemple pour alpin.

    FROM java:8u111-jdk-alpine
    RUN apk add gnupg maven git --update-cache 
    --repository http://dl-4.alpinelinux.org/alpine/edge/community/ --allow-untrusted && 
    mkdir ~/.m2/

  • Assembler un conteneur pour votre projet

    docker build -t registry.gitlab.com/group/deploy .

  • Nous authentifions et chargeons le conteneur dans le registre.

    docker login -u USER -p PASSWORD registry.gitlab.com
    docker push registry.gitlab.com/group/deploy

Au contenu

CI GitLab

Déployer le projet

Ajoutez le fichier .gitlab-ci.yml à la racine du projet de déploiement
Le script présente deux tâches de déploiement mutuellement exclusives. Coureur spécifique ou coureur partagé respectivement.

.gitlab-ci.yml

stages:
  - deploy

Specific Runner:
  extends: .java_deploy_template
  # Задача будет выполняться на вашем shell-раннере
  tags:
    - deploy

Shared Runner:
  extends: .java_deploy_template
  # Задача будет выполняться на публичном docker-раннере
  tags:
    - docker
  # Образ из раздела GitLab Runner -> Shared Runner -> Docker
  image: registry.gitlab.com/group/deploy-project:latest
  before_script:
    # Импортируем GPG ключ
    - printf "${GPG_SECRET_KEY}" | gpg --batch --import
    # Сохраняем maven конфигурацию
    - printf "${SETTINGS_SECURITY_XML}" > ~/.m2/settings-security.xml
    - printf "${SETTINGS_XML}" > ~/.m2/settings.xml

.java_deploy_template:
  stage: deploy
  # Задача сработает по триггеру, если передана переменная DEPLOY со значением java
  only:
    variables:
    - $DEPLOY == "java"
  variables:
    # отключаем клонирование текущего проекта
    GIT_STRATEGY: none
  script:
    # Предоставляем возможность хранения пароля в незашифрованном виде
    - git config --global credential.helper store
    # Сохраняем временные креды пользователя gitlab-ci-token
    # Токен работает для всех публичных проектов gitlab.com и для проектов группы
    - echo "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com" >> ~/.git-credentials
    # Полностью чистим текущую директорию
    - rm -rf .* *
    # Клонируем проект который, будем деплоить в Sonatype Nexus
    - git clone ${DEPLOY_CI_REPOSITORY_URL} .
    # Переключаемся на нужный коммит
    - git checkout ${DEPLOY_CI_COMMIT_SHA} -f
    # Если хоть один pom.xml содержит параметр autoReleaseAfterClose валим сборку.
    # В противном случае есть риск залить сырые артефакты в maven central
    - >
      for pom in $(find . -name pom.xml); do
        if [[ $(grep -q autoReleaseAfterClose "$pom" && echo $?) == 0 ]]; then
          echo "File $pom contains prohibited setting: <autoReleaseAfterClose>";
          exit 1;
        fi;
      done
    # Если параметр DEPLOY_CI_COMMIT_TAG пустой, то принудительно ставим SNAPSHOT-версию
    - >
      if [[ "${DEPLOY_CI_COMMIT_TAG}" != "" ]]; then
        mvn versions:set -DnewVersion=${DEPLOY_CI_COMMIT_TAG}
      else
        VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
        if [[ "${VERSION}" == *-SNAPSHOT ]]; then
          mvn versions:set -DnewVersion=${VERSION}
        else
          mvn versions:set -DnewVersion=${VERSION}-SNAPSHOT
        fi
      fi
    # Запускаем задачу на сборку и деплой артефактов
    - mvn clean deploy -DskipTests=true

Au contenu

Projet Java

Dans les projets Java censés être téléchargés sur des référentiels publics, vous devez ajouter 2 étapes pour télécharger les versions Release et Snapshot.

.gitlab-ci.yml

stages:
  - build
  - test
  - verify
  - deploy

<...>

Release:
  extends: .trigger_deploy
  # Запускать задачу только пo тегу.
  only:
    - tags

Snapshot:
  extends: .trigger_deploy
  # Запускаем задачу на публикацию SNAPSHOT версии вручную
  when: manual
  # Не запускать задачу, если проставлен тег.
  except:
    - tags

.trigger_deploy:
  stage: deploy
  variables:
    # Отключаем клонирование текущего проекта
    GIT_STRATEGY: none
    # Ссылка на триггер deploy-задачи
    URL: "https://gitlab.com/api/v4/projects/<deploy project ID>/trigger/pipeline"
    # Переменные deploy-задачи
    POST_DATA: "
      token=${DEPLOY_TOKEN}&
      ref=master&
      variables[DEPLOY]=${DEPLOY}&
      variables[DEPLOY_CI_REPOSITORY_URL]=${CI_REPOSITORY_URL}&
      variables[DEPLOY_CI_PROJECT_NAME]=${CI_PROJECT_NAME}&
      variables[DEPLOY_CI_COMMIT_SHA]=${CI_COMMIT_SHA}&
      variables[DEPLOY_CI_COMMIT_TAG]=${CI_COMMIT_TAG}
      "
  script:
    # Не использую cURL, так как с флагами --fail --show-error
    # он не выводит тело ответа, если HTTP код 400 и более 
    - wget --content-on-error -qO- ${URL} --post-data ${POST_DATA}

Dans cette solution, je suis allé un peu plus loin et j'ai décidé d'utiliser un modèle CI pour les projets Java.

Plus de détails

J'ai créé un projet séparé gitlab-ci dans lequel j'ai placé un modèle CI pour les projets Java commun.yml.

commun.yml

stages:
  - build
  - test
  - verify
  - deploy

variables:
  SONAR_ARGS: "
  -Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA} 
  -Dsonar.gitlab.ref_name=${CI_COMMIT_REF_NAME} 
  "

.build_java_project:
  stage: build
  tags:
    - touchbit-shell
  variables:
    SKIP_TEST: "false"
  script:
    - mvn clean
    - mvn package -DskipTests=${SKIP_TEST}
  artifacts:
    when: always
    expire_in: 30 day
    paths:
      - "*/target/reports"

.build_sphinx_doc:
  stage: build
  tags:
    - touchbit-shell
  variables:
    DOCKERFILE: .indirect/docs/Dockerfile
  script:
    - docker build --no-cache -t ${CI_PROJECT_NAME}/doc -f ${DOCKERFILE} .

.junit_module_test_run:
  stage: test
  tags:
    - touchbit-shell
  variables:
    MODULE: ""
  script:
    - cd ${MODULE}
    - mvn test
  artifacts:
    when: always
    expire_in: 30 day
    paths:
      - "*/target/reports"

.junit_test_run:
  stage: test
  tags:
    - touchbit-shell
  script:
    - mvn test
  artifacts:
    when: always
    expire_in: 30 day
    paths:
    - "*/target/reports"

.sonar_review:
  stage: verify
  tags:
    - touchbit-shell
  dependencies: []
  script:
    - >
      if [ "$CI_BUILD_REF_NAME" == "master" ]; then
        mvn compile sonar:sonar -Dsonar.login=$SONAR_LOGIN $SONAR_ARGS
      else
        mvn compile sonar:sonar -Dsonar.login=$SONAR_LOGIN $SONAR_ARGS -Dsonar.analysis.mode=preview
      fi

.trigger_deploy:
  stage: deploy
  tags:
    - touchbit-shell
  variables:
    URL: "https://gitlab.com/api/v4/projects/10345765/trigger/pipeline"
    POST_DATA: "
      token=${DEPLOY_TOKEN}&
      ref=master&
      variables[DEPLOY]=${DEPLOY}&
      variables[DEPLOY_CI_REPOSITORY_URL]=${CI_REPOSITORY_URL}&
      variables[DEPLOY_CI_PROJECT_NAME]=${CI_PROJECT_NAME}&
      variables[DEPLOY_CI_COMMIT_SHA]=${CI_COMMIT_SHA}&
      variables[DEPLOY_CI_COMMIT_TAG]=${CI_COMMIT_TAG}
      "
  script:
  - wget --content-on-error -qO- ${URL} --post-data ${POST_DATA}

.trigger_release_deploy:
  extends: .trigger_deploy
  only:
    - tags

.trigger_snapshot_deploy:
  extends: .trigger_deploy
  when: manual
  except:
    - tags

En conséquence, dans les projets Java eux-mêmes, .gitlab-ci.yml semble très compact et peu verbeux

.gitlab-ci.yml

include: https://gitlab.com/TouchBIT/gitlab-ci/raw/master/common.yml

Shields4J:
  extends: .build_java_project

Sphinx doc:
  extends: .build_sphinx_doc
  variables:
    DOCKERFILE: .docs/Dockerfile

Sonar review:
  extends: .sonar_review
  dependencies:
    - Shields4J

Release:
  extends: .trigger_release_deploy

Snapshot:
  extends: .trigger_snapshot_deploy

Au contenu

Configuration Pom.xml

Ce sujet est décrit de manière très détaillée. Googleplex в Configuration de Maven pour signer et télécharger automatiquement des artefacts vers des référentiels d'instantanés et de transfert, je vais donc décrire certaines des nuances de l'utilisation des plugins. Je décrirai également à quel point vous pouvez utiliser facilement et de manière détendue nexus-staging-maven-pluginsi vous ne voulez pas ou ne pouvez pas utiliser org.sonatype.oss:oss-parent comme parent pour votre projet.

plugin d'installation maven

Installe les modules dans le référentiel local.
Très utile pour la vérification locale des solutions dans d'autres projets, ainsi qu'une somme de contrôle.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-install-plugin</artifactId>
  <executions>
    <execution>
      <id>install-project</id>
      <!-- Если у вас многомодульный проект с деплоем родительского помика -->
      <phase>install</phase>
      <!-- Явно указываем файлы для локальной установки -->
      <configuration>
        <file>target/${project.artifactId}-${project.version}.jar</file>
```target/${project.artifactId}-${project.version}-sources.jar</sources>
        <pomFile>dependency-reduced-pom.xml</pomFile>
        <!-- Принудительное обновление метаданных проекта -->
        <updateReleaseInfo>true</updateReleaseInfo>
        <!-- Контрольные суммы для проверки целостности -->
        <createChecksum>true</createChecksum>
      </configuration>
    </execution>
  </executions>
</plugin>

Au contenu

plugin-maven-javadoc

Génération de javadoc pour le projet.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-javadoc-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>jar</goal>
      </goals>
      <!-- Генерация javadoc должна быть после фазы генерации ресурсов -->
      <phase>prepare-package</phase>
      <configuration>
        <!-- Очень помогает в публичных проектах -->
        <failOnError>true</failOnError>
        <failOnWarnings>true</failOnWarnings>
        <!-- Убирает ошибку поиска документации в target директории -->
        <detectOfflineLinks>false</detectOfflineLinks>
      </configuration>
    </execution>
  </executions>
</plugin>

Si vous disposez d'un module qui ne contient pas de java (par exemple uniquement des ressources)
Ou vous ne voulez pas générer de javadoc en principe, alors aidez maven-jar-plugin

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <executions>
    <execution>
      <id>empty-javadoc-jar</id>
      <phase>generate-resources</phase>
      <goals>
        <goal>jar</goal>
      </goals>
      <configuration>
        <classifier>javadoc</classifier>
        <classesDirectory>${basedir}/javadoc</classesDirectory>
      </configuration>
    </execution>
  </executions>
</plugin>

Au contenu

maven-gpg-plugin

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-gpg-plugin</artifactId>
  <executions>
    <execution>
      <id>sign-artifacts</id>
      <!-- Сборка будет падать, если отсутствует GPG ключ -->
      <!-- Подписываем артефакты только на фазе deploy -->
      <phase>deploy</phase>
      <goals>
        <goal>sign</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Au contenu

Nexus-staging-maven-plugin

Configuration:

<project>
  <!-- ... -->
  <build>
    <plugins>
      <!-- ... -->
      <plugin>
        <groupId>org.sonatype.plugins</groupId>
        <artifactId>nexus-staging-maven-plugin</artifactId>
      </plugin>
    </plugins>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.sonatype.plugins</groupId>
          <artifactId>nexus-staging-maven-plugin</artifactId>
          <extensions>true</extensions>
          <configuration>
            <serverId>sonatype</serverId>
            <nexusUrl>https://oss.sonatype.org/</nexusUrl>
            <!-- Обновляем метаданные, чтобы пометить артефакт как release -->
            <!-- Не влияет на snapshot версии -->
            <updateReleaseInfo>true</updateReleaseInfo>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-deploy-plugin</artifactId>
          <configuration>
            <!-- Отключаем плагин -->
            <skip>true</skip>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
  <distributionManagement>
    <snapshotRepository>
      <id>sonatype</id>
      <name>Nexus Snapshot Repository</name>
      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </snapshotRepository>
    <repository>
      <id>sonatype</id>
      <name>Nexus Release Repository</name>
      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
  </distributionManagement>
</project>

Si vous avez un projet multi-modules et que vous n'avez pas besoin de télécharger un module spécifique dans le référentiel, vous devez alors ajouter nexus-staging-maven-plugin avec drapeau skipNexusStagingDeployMojo

<build>
  <plugins>
    <plugin>
      <groupId>org.sonatype.plugins</groupId>
      <artifactId>nexus-staging-maven-plugin</artifactId>
      <configuration>
        <skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
      </configuration>
    </plugin>
  </plugins>
</build>

Après le téléchargement, les versions instantanées/release sont disponibles dans référentiels de préparation

<repositories>
  <repository>
    <id>SonatypeNexus</id>
    <url>https://oss.sonatype.org/content/groups/staging/</url>
    <!-- Не надо указывать флаги snapshot/release для репозитория -->
  </repository>
</repositories>

Plus d'atouts

  • Une liste très riche d'objectifs pour travailler avec le référentiel Nexus (mvn help:describe -Dplugin=org.sonatype.plugins:nexus-staging-maven-plugin).
  • Vérification automatique de la version pour le téléchargement sur Maven Central

Au contenu

Résultat

Publication de la version SNAPSHOT

Lors de la construction d'un projet, il est possible de lancer manuellement une tâche pour télécharger la version SNAPSHOT sur Nexus

Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

Lorsque cette tâche est lancée, la tâche correspondante dans le projet de déploiement est déclenchée (exemple).

Journal découpé

Running with gitlab-runner 11.10.0 (3001a600)
  on Deploy runner JSKWyxUw
Using Shell executor...
Running on ih1174328.vds.myihor.ru...
Skipping Git repository setup
Skipping Git checkout
Skipping Git submodules setup
$ rm -rf .* *
$ git config --global credential.helper store
$ echo "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com" >> ~/.git-credentials
$ git clone ${DEPLOY_CI_REPOSITORY_URL} .
Cloning into 'shields4j'...
$ git checkout ${DEPLOY_CI_COMMIT_SHA}
Note: checking out '850f86aa317194395c5387790da1350e437125a7'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
  git checkout -b new_branch_name
HEAD is now at 850f86a... skip deploy test-core
$ for pom in $(find . -name pom.xml); do # collapsed multi-line command
$ if [[ "${DEPLOY_CI_COMMIT_TAG}" != "" ]]; then # collapsed multi-line command
[INFO] Scanning for projects...
[INFO] Inspecting build with total of 4 modules...
[INFO] Installing Nexus Staging features:
[INFO]   ... total of 4 executions of maven-deploy-plugin replaced with nexus-staging-maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] Shields4J                                                          [pom]
[INFO] test-core                                                          [jar]
[INFO] Shields4J client                                                   [jar]
[INFO] TestNG listener                                                    [jar]
[INFO] 
[INFO] --------------< org.touchbit.shields4j:shields4j-parent >---------------
[INFO] Building Shields4J 1.0.0                                           [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] --- versions-maven-plugin:2.5:set (default-cli) @ shields4j-parent ---
[INFO] Searching for local aggregator root...
[INFO] Local aggregation root: /home/gitlab-deployer/JSKWyxUw/0/TouchBIT/deploy/shields4j
[INFO] Processing change of org.touchbit.shields4j:shields4j-parent:1.0.0 -> 1.0.0-SNAPSHOT
[INFO] Processing org.touchbit.shields4j:shields4j-parent
[INFO]     Updating project org.touchbit.shields4j:shields4j-parent
[INFO]         from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO] 
[INFO] Processing org.touchbit.shields4j:client
[INFO]     Updating parent org.touchbit.shields4j:shields4j-parent
[INFO]         from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO]     Updating dependency org.touchbit.shields4j:test-core
[INFO]         from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO] 
[INFO] Processing org.touchbit.shields4j:test-core
[INFO]     Updating parent org.touchbit.shields4j:shields4j-parent
[INFO]         from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO] 
[INFO] Processing org.touchbit.shields4j:testng
[INFO]     Updating parent org.touchbit.shields4j:shields4j-parent
[INFO]         from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO]     Updating dependency org.touchbit.shields4j:client
[INFO]         from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO]     Updating dependency org.touchbit.shields4j:test-core
[INFO]         from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] Shields4J 1.0.0 .................................... SUCCESS [  0.992 s]
[INFO] test-core .......................................... SKIPPED
[INFO] Shields4J client ................................... SKIPPED
[INFO] TestNG listener 1.0.0 .............................. SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.483 s
[INFO] Finished at: 2019-04-21T02:40:42+03:00
[INFO] ------------------------------------------------------------------------
$ mvn clean deploy -DskipTests=${SKIP_TESTS}
[INFO] Scanning for projects...
[INFO] Inspecting build with total of 4 modules...
[INFO] Installing Nexus Staging features:
[INFO]   ... total of 4 executions of maven-deploy-plugin replaced with nexus-staging-maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] Shields4J                                                          [pom]
[INFO] test-core                                                          [jar]
[INFO] Shields4J client                                                   [jar]
[INFO] TestNG listener                                                    [jar]
[INFO] 
[INFO] --------------< org.touchbit.shields4j:shields4j-parent >---------------
[INFO] Building Shields4J 1.0.0-SNAPSHOT                                  [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
...
DELETED
...
[INFO]  * Bulk deploy of locally gathered snapshot artifacts finished.
[INFO] Remote deploy finished with success.
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] Shields4J 1.0.0-SNAPSHOT ........................... SUCCESS [  2.375 s]
[INFO] test-core .......................................... SUCCESS [  3.929 s]
[INFO] Shields4J client ................................... SUCCESS [  3.815 s]
[INFO] TestNG listener 1.0.0-SNAPSHOT ..................... SUCCESS [ 36.134 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 47.629 s
[INFO] Finished at: 2019-04-21T02:41:32+03:00
[INFO] ------------------------------------------------------------------------

En conséquence, la version est chargée dans Nexus 1.0.0-INSTANTANÉ.

Toutes les versions d'instantanés peuvent être supprimées du référentiel sur le site Web oss.sonatype.org sous votre compte.

Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

Au contenu

Publication d'une version commerciale

Lorsqu'une balise est installée, la tâche correspondante dans le projet de déploiement est automatiquement déclenchée pour télécharger la version finale sur Nexus (exemple).

Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

La meilleure partie est que la version rapprochée est automatiquement déclenchée dans Nexus.

[INFO] Performing remote staging...
[INFO] 
[INFO]  * Remote staging into staging profile ID "9043b43f77dcc9"
[INFO]  * Created staging repository with ID "orgtouchbit-1037".
[INFO]  * Staging repository at https://oss.sonatype.org:443/service/local/staging/deployByRepositoryId/orgtouchbit-1037
[INFO]  * Uploading locally staged artifacts to profile org.touchbit
[INFO]  * Upload of locally staged artifacts finished.
[INFO]  * Closing staging repository with ID "orgtouchbit-1037".
Waiting for operation to complete...
.........
[INFO] Remote staged 1 repositories, finished with success.
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] Shields4J 1.0.0 .................................... SUCCESS [  9.603 s]
[INFO] test-core .......................................... SUCCESS [  3.419 s]
[INFO] Shields4J client ................................... SUCCESS [  9.793 s]
[INFO] TestNG listener 1.0.0 .............................. SUCCESS [01:23 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:47 min
[INFO] Finished at: 2019-04-21T04:05:46+03:00
[INFO] ------------------------------------------------------------------------

Et si quelque chose ne va pas, la tâche échouera définitivement

[INFO] Performing remote staging...
[INFO] 
[INFO]  * Remote staging into staging profile ID "9043b43f77dcc9"
[INFO]  * Created staging repository with ID "orgtouchbit-1038".
[INFO]  * Staging repository at https://oss.sonatype.org:443/service/local/staging/deployByRepositoryId/orgtouchbit-1038
[INFO]  * Uploading locally staged artifacts to profile org.touchbit
[INFO]  * Upload of locally staged artifacts finished.
[INFO]  * Closing staging repository with ID "orgtouchbit-1038".
Waiting for operation to complete...
.......
[ERROR] Rule failure while trying to close staging repository with ID "orgtouchbit-1039".
[ERROR] 
[ERROR] Nexus Staging Rules Failure Report
[ERROR] ==================================
[ERROR] 
[ERROR] Repository "orgtouchbit-1039" failures
[ERROR]   Rule "signature-staging" failures
[ERROR]     * No public key: Key with id: (1f42b618d1cbe1b5) was not able to be located on &lt;a href=http://keys.gnupg.net:11371/&gt;http://keys.gnupg.net:11371/&lt;/a&gt;. Upload your public key and try the operation again.
...
[ERROR] Cleaning up local stage directory after a Rule failure during close of staging repositories: [orgtouchbit-1039]
[ERROR]  * Deleting context 9043b43f77dcc9.properties
[ERROR] Cleaning up remote stage repositories after a Rule failure during close of staging repositories: [orgtouchbit-1039]
[ERROR]  * Dropping failed staging repository with ID "orgtouchbit-1039" (Rule failure during close of staging repositories: [orgtouchbit-1039]).
[ERROR] Remote staging finished with a failure: Staging rules failure!
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] Shields4J 1.0.0 .................................... SUCCESS [  4.073 s]
[INFO] test-core .......................................... SUCCESS [  2.788 s]
[INFO] Shields4J client ................................... SUCCESS [  3.962 s]
[INFO] TestNG listener 1.0.0 .............................. FAILURE [01:07 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

En conséquence, nous n’avons qu’un seul choix. Soit supprimez cette version, soit publiez-la.

Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

Après leur publication, après un certain temps, les artefacts seront disponibles Configuration de GitLab CI pour télécharger un projet Java sur Maven Central

offtop

Ce fut une découverte pour moi que Maven indexe d'autres référentiels publics.
J'ai dû ajouter robots.txt car il indexait mon ancien référentiel.

Au contenu

Conclusion

Ce que nous avons

  • Un projet de déploiement distinct dans lequel vous pouvez implémenter plusieurs tâches CI pour télécharger des artefacts vers des référentiels publics pour différents langages de développement.
  • Le projet Deploy est isolé des interférences extérieures et ne peut être modifié que par les utilisateurs disposant des rôles Propriétaire et Mainteneur.
  • Un exécuteur spécifique distinct avec un cache « chaud » pour exécuter uniquement les tâches de déploiement.
  • Publication de versions d'instantanés/versions dans un référentiel public.
  • Vérification automatique de la version finale pour la préparation à la publication dans Maven Central.
  • Protection contre la publication automatique de versions « brutes » dans maven central.
  • Créez et publiez des versions instantanées « en un clic ».
  • Un référentiel unique pour obtenir des versions instantanées/release.
  • Pipeline général pour créer/tester/publier un projet Java.

La configuration de GitLab CI n'est pas un sujet aussi compliqué qu'il y paraît à première vue. Il suffit de mettre en place CI clé en main à plusieurs reprises, et maintenant vous êtes loin d'être un amateur en la matière. De plus, la documentation GitLab est très redondante. N'ayez pas peur de faire le premier pas. La route apparaît sous les pas de la personne qui marche (je ne me souviens plus qui l'a dit :)

Je serai heureux de recevoir des commentaires.

Dans le prochain article, je parlerai de la façon de configurer GitLab CI pour exécuter des tâches avec des tests d'intégration de manière compétitive (exécuter les services testés à l'aide de docker-compose) si vous n'avez qu'un seul exécuteur de shell.

Au contenu

Source: habr.com

Ajouter un commentaire