Configuração do GitLab CI para o upload de projeto Java no Maven Central

Este artigo é destinado a desenvolvedores Java que precisam publicar rapidamente seus produtos em repositórios Sonatype e/ou Maven Central usando o GitLab. Neste artigo, falarei sobre a configuração do gitlab-runner, gitlab-ci e maven-plugin para resolver essa tarefa.

Pré-requisitos:

  • Armazenamento seguro das chaves mvn e GPG.
  • Execução segura de tarefas públicas de CI.
  • Carregamento de artefatos (release/snapshot) em repositórios públicos.
  • Verificação automática de versões de release para publicação no Maven Central.
  • Solução geral para o carregamento de artefatos em um repositório para vários projetos.
  • Simples e fácil de usar.

Conteúdo

Informações Gerais

  • Uma descrição detalhada do mecanismo de publicação de artefatos no Maven Central através do Serviço de Hospedagem de Repositórios OSS da Sonatype já foi descrita em este artigo pelo usuário Googolplex, portanto, em locais necessários, farei referência a este artigo.
  • Primeiro, registre-se em Sonatype JIRA e crie um ticket para abrir o repositório (leia mais na seção Criação de ticket no Sonatype JIRA). Após abrir o repositório, um par de login/senha do JIRA (daqui em diante, conta Sonatype) será utilizado para carregar artefatos no Sonatype Nexus.
  • A seguir, o processo de geração da chave GPG é descrito de forma bem direta. Para mais detalhes, veja a seção Configuração do GnuPG para assinatura de artefatos
  • Se você estiver usando o console Linux para gerar a chave GPG (gnupg/gnupg2), é necessário instalar rng-tools para gerar entropia. Caso contrário, a geração da chave pode demorar muito.
  • Serviços de armazenamento públicos chaves GPG

Para o conteúdo

Configuração do projeto de deploy no GitLab

  • Em primeiro lugar, é necessário criar e configurar um projeto onde o pipeline será armazenado para o deploy dos artefatos. O meu projeto eu nomeei de forma simples — deploy
  • Depois de criar o repositório, é necessário restrigir o acesso à edição do repositório.
    Vamos para o projeto -> Configurações -> Repositório -> Ramos Protegidos. Remove todas as regras e adiciona uma única regra com Wildcard * com permissão para push e merge apenas para usuários com o papel de Maintainers. Esta regra irá funcionar para todos os usuários tanto deste projeto quanto do grupo ao qual este projeto pertence.
    Configuração do GitLab CI para o upload de projeto Java no Maven Central
  • Se houver mais de um mantenedor, a melhor solução será restringir o acesso ao projeto em princípio.
    Vamos para o projeto -> Configurações -> Geral -> Visibilidade, recursos do projeto, permissões e definimos a Visibilidade do projeto como Privado.
    Meu projeto está disponível publicamente, pois estou usando meu próprio GitLab Runner e apenas eu tenho acesso para modificar o repositório. Além disso, não é do meu interesse expor informações privadas nos logs públicos do pipeline.
  • Aperto das regras para modificação do repositório
    Vamos para o projeto -> Configurações -> Repositório -> Regras de Push e ativamos as opções Restrição do Commit, Verificar se o autor é um usuário GitLab. Também recomendo configurar a assinatura de commits, e ativar a opção Rejeitar commits não assinados.
  • Em seguida, precisamos configurar um gatilho para executar tarefas
    Vamos para o projeto -> Configurações -> CI / CD -> Gatilhos de pipeline e criamos um novo token de gatilho
    Esse token pode ser adicionado imediatamente à configuração geral de variáveis para o grupo de projetos.
    Vamos para o grupo -> Configurações -> CI / CD -> Variáveis e adicionamos a variável DEPLOY_TOKEN com o token do gatilho como valor.

Para o conteúdo

GitLab Runner

Nesta seção está descrita a configuração para executar tarefas de deploy usando runner próprio (Específico) e público (Compartilhado).

Runner Específico

Eu uso runners próprios, pois em primeiro lugar é conveniente, rápido e barato.
Para o runner, recomendo um VDS Linux com 1 CPU, 2 GB de RAM, 20 GB de HDD. O custo é cerca de 3000₽ por ano.

Meu runner

Para o runner, eu escolhi um VDS com 4 CPUs, 4 GB de RAM, 50 GB de SSD. Custou cerca de 11000₽ e nunca me arrependi.
No total, tenho 7 máquinas. 5 na Aruba e 2 na Ihor.

Então, temos um runner. Agora vamos configurá-lo.
Entramos na máquina via SSH e instalamos java, git, maven, gnupg2.

Para o conteúdo

Instalamos o gitlab runner

  • Criamos um novo grupo runner
    sudo groupadd runner
  • Criamos um diretório para o cache do maven e configuramos as permissões do grupo runner
    Este passo pode ser pulado se você não planeja executar vários runners em uma única máquina.
    mkdir -p /usr/cache/.m2/repository
    chown -R :runner /usr/cache
    chmod -R 770 /usr/cache
  • Criamos o usuário gitlab-deployer e o adicionamos ao grupo runner
    useradd -m -d /home/gitlab-deployer gitlab-deployer
    usermod -a -G runner gitlab-deployer
  • Adicionamos ao arquivo /etc/ssh/sshd_config a seguinte linha
    Permitir Usuários root@* gitlab-deployer@127.0.0.1
  • Reiniciando sshd.
    systemctl restart sshd
  • Definindo a senha para o usuário gitlab-deployer (pode ser simples, pois há uma limitação para localhost)
    passwd gitlab-deployer
  • Instalando o 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
  • Acessar o site gitlab.com -> deploy-project -> Configurações -> CI/CD -> Runners -> Runners Específicos e copiar o token de registro

Captura de tela

Configuração do GitLab CI para o upload de projeto Java no Maven Central

  • Registrando o runner
    gitlab-runner register --config /etc/gitlab-runner/gitlab-deployer-config.toml

Processo

Plataforma de execução arch=amd64 os=linux pid=17594 revisão=3001a600 versão=11.10.0
Executando em modo de sistema.
Por favor, insira a URL do coordenador gitlab-ci (ex: https://gitlab.com/):
https://gitlab.com/
Por favor, insira o token gitlab-ci para este runner:
REGISTRATION_TOKEN
Por favor, insira a descrição gitlab-ci para este runner:
[ih1174328.vds.myihor.ru]: Deploy Runner
Por favor, insira as tags gitlab-ci para este runner (separadas por vírgula):
deploy
Registrando runner... bem-sucedido                     runner=ZvKdjJhx
Por favor, insira o executor: docker-ssh, parallels, virtualbox, docker-ssh+machine, kubernetes, docker, ssh, docker+machine, shell:
shell
Runner registrado com sucesso. Sinta-se à vontade para iniciá-lo, mas se já estiver em execução, a configuração deverá ser recarregada automaticamente!
  • Verificando se o runner está registrado. Acessar o site gitlab.com -> deploy-project -> Configurações -> CI/CD -> Runners -> Runners Específicos -> Runners ativados para este projeto

Captura de tela

Configuração do GitLab CI para o upload de projeto Java no Maven Central

  • Adicionamos separado serviço /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
  • Iniciando o serviço.
    systemctl enable gitlab-deployer.service
    systemctl start gitlab-deployer.service
    systemctl status gitlab-deployer.service
  • Verificando se o runner está em execução.

Exemplo

Configuração do GitLab CI para o upload de projeto Java no Maven Central

Para o conteúdo

Geração de chaves GPG

  • Acessando a máquina via ssh como usuário gitlab-deployer (isso é importante para gerar a chave GPG)
    ssh gitlab-deployer@127.0.0.1
  • Gerando a chave respondendo às perguntas. Eu usei meu próprio nome e e-mail.
    Certifique-se de especificar uma senha para a chave. Esta chave será usada para assinar artefatos.
    gpg --gen-key 
  • Verificamos
    gpg --list-keys -a
    /home/gitlab-deployer/.gnupg/pubring.gpg
    ----------------------------------------
    pub   4096R/00000000 2019-04-19
    uid                  Petruha Petrov 
    sub   4096R/11111111 2019-04-19
  • Carregando nossa chave pública no servidor de chaves
    gpg --keyserver keys.gnupg.net --send-key 00000000
    gpg: sending key 00000000 to hkp server keys.gnupg.net

Para o conteúdo

Configuração do Maven

  • Acessando como usuário gitlab-deployer
    su gitlab-deployer 
  • Criando diretório maven repository e vinculando com o cache (não erre)
    Este item pode ser ignorado se você não planeja executar vários runners na mesma máquina.
    mkdir -p ~/.m2/repository
    ln -s /usr/cache/.m2/repository /home/gitlab-deployer/.m2/repository
  • Criando a chave mestre
    mvn --encrypt-master-password password
    {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}
  • Criando o arquivo ~/.m2/settings-security.xml
    {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}
  • Criptografando a senha da conta Sonatype
    mvn --encrypt-password SONATYPE_PASSWORD
    {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}
  • Criando o arquivo ~/.m2/settings.xml
    env
            
                true
            
            
                GPG_SECRET_KEY_PASSPHRASE
            
        
    
    
        
            sonatype
            SONATYPE_USERNAME
            {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}

onde,
GPG_SECRET_KEY_PASSPHRASE — a senha da chave GPG
SONATYPE_USERNAME — login da conta sonatype

Aqui a configuração do runner está concluída, podemos passar para a seção GitLab CI

Para o conteúdo

Runner Compartilhado

Geração de chaves GPG

  • Primeiro, é necessário criar uma chave GPG. Para isso, instalamos o gnupg.
    yum install -y gnupg
  • Geramos a chave respondendo às perguntas. Eu usei meu próprio nome e e-mail. É obrigatório indicar uma senha para a chave.
    gpg --gen-key 
  • Exibimos informações sobre a chave
    gpg --list-keys -a
    pub   rsa3072 2019-04-24 [SC] [expires: 2021-04-23]
      2D0D1706366FC4AEF79669E24D09C55BBA3FD728
    uid           [ultimate] tttemp 
    sub   rsa3072 2019-04-24 [E] [expires: none]
  • Carregando nossa chave pública no servidor de chaves
    gpg --keyserver keys.gnupg.net --send-key 2D0D1706366FC4AEF79669E24D09C55BBA3FD728
    gpg: sending key 2D0D1706366FC4AEF79669E24D09C55BBA3FD728 to hkp server keys.gnupg.net
  • Obtendo a chave privada
    gpg --export-secret-keys --armor 2D0D1706366FC4AEF79669E24D09C55BBA3FD728
    -----BEGIN PGP PRIVATE KEY BLOCK-----
    lQWGBFzAqp8BDADN41CPwJ/gQwiKEbyA902DKw/WSB1AvZQvV/ZFV77xGeG4K7k5
    ...
    =2Wd2
    -----END PGP PRIVATE KEY BLOCK-----
  • Acesse as configurações do projeto -> Configurações -> CI / CD -> Variáveis e salve a chave privada na variável GPG_SECRET_KEY
    Configuração do GitLab CI para o upload de projeto Java no Maven Central

Para o conteúdo

Configuração do Maven

  • Criando a chave mestre
    mvn --encrypt-master-password password
    {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}
  • Acesse as configurações do projeto -> Configurações -> CI / CD -> Variáveis e salve na variável SETTINGS_SECURITY_XML as seguintes linhas:
    {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=}
  • Criptografando a senha da conta Sonatype
    mvn --encrypt-password SONATYPE_PASSWORD
    {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}
  • Acesse as configurações do projeto -> Configurações -> CI / CD -> Variáveis e salve na variável SETTINGS_XML as seguintes linhas:
    env
            
                true
            
            
                GPG_SECRET_KEY_PASSPHRASE
            
        
    
    
        
            sonatype
            sonatype_username
            {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}

onde,
GPG_SECRET_KEY_PASSPHRASE — a senha da chave GPG
SONATYPE_USERNAME — login da conta sonatype

Para o conteúdo

Deploy da imagem Docker

  • Criamos um Dockerfile bem simples para executar tarefas de deploy com a versão necessária do Java. Abaixo está um exemplo para alpine.
    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/
  • Construindo o contêiner para o seu projeto
    docker build -t registry.gitlab.com/group/deploy .
  • Autenticamo-nos e fazemos o upload do contêiner no registry.
    docker login -u USER -p PASSWORD registry.gitlab.com
    docker push registry.gitlab.com/group/deploy

Para o conteúdo

GitLab CI

Deploy do projeto

Adicionamos o arquivo .gitlab-ci.yml na raiz do projeto de implantação
O script contém duas tarefas mutuamente exclusivas para a implantação. Specific Runner ou Shared Runner, respectivamente.

.gitlab-ci.yml

stages:
  - deploy

Specific Runner:
  extends: .java_deploy_template
  # A tarefa será executada no seu shell-runner
  tags:
    - deploy

Shared Runner:
  extends: .java_deploy_template
  # A tarefa será executada no docker-runner público
  tags:
    - docker
  # Imagem da seção GitLab Runner -> Shared Runner -> Docker
  image: registry.gitlab.com/group/deploy-project:latest
  before_script:
    # Importamos a chave GPG
    - printf "${GPG_SECRET_KEY}" | gpg --batch --import
    # Salvamos a configuração do maven
    - printf "${SETTINGS_SECURITY_XML}" > ~/.m2/settings-security.xml
    - printf "${SETTINGS_XML}" > ~/.m2/settings.xml

.java_deploy_template:
  stage: deploy
  # A tarefa será acionada por um trigger, se a variável DEPLOY tiver o valor java
  only:
    variables:
    - $DEPLOY == "java"
  variables:
    # desativamos a clonagem do projeto atual
    GIT_STRATEGY: none
  script:
    # Permitimos o armazenamento da senha em texto não criptografado
    - git config --global credential.helper store
    # Salvamos as credenciais temporárias do usuário gitlab-ci-token
    # O token funciona para todos os projetos públicos gitlab.com e para projetos de grupo
    - echo "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com" >> ~/.git-credentials
    # Limpamos completamente o diretório atual
    - rm -rf .* *
    # Clonamos o projeto que vamos implantar no Sonatype Nexus
    - git clone ${DEPLOY_CI_REPOSITORY_URL} .
    # Mudamos para o commit desejado
    - git checkout ${DEPLOY_CI_COMMIT_SHA} -f
    # Se algum pom.xml contiver o parâmetro autoReleaseAfterClose, cancelamos a construção.
    # Caso contrário, há o risco de enviar artefatos não finalizados para o maven central
    - >
      for pom in $(find . -name pom.xml); do
        if [[ $(grep -q autoReleaseAfterClose "$pom" && echo $?) == 0 ]]; then
          echo "Arquivo $pom contém a configuração proibida: ";
          exit 1;
        fi;
      done
    # Se o parâmetro DEPLOY_CI_COMMIT_TAG estiver vazio, definimos forçadamente a versão 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
    # Executamos a tarefa de construção e implantação dos artefatos
    - mvn clean deploy -DskipTests=true

Para o conteúdo

Projeto Java

Em projetos Java que se pretende carregar em repositórios públicos, é necessário adicionar 2 etapas para carregar as versões Release e Snapshot.

.gitlab-ci.yml

stages:
  - build
  - test
  - verify
  - deploy



Release:
  extends: .trigger_deploy
  # Executar a tarefa apenas por tag.
  only:
    - tags

Snapshot:
  extends: .trigger_deploy
  # Executamos a tarefa para publicar a versão SNAPSHOT manualmente
  when: manual
  # Não executar a tarefa se uma tag estiver definida.
  except:
    - tags

.trigger_deploy:
  stage: deploy
  variables:
    # Desativamos a clonagem do projeto atual
    GIT_STRATEGY: none
    # Link para o gatilho da tarefa de deploy
    URL: "https://gitlab.com/api/v4/projects//trigger/pipeline"
    # Variáveis da tarefa de 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:
    # Não uso cURL, pois com as flags --fail --show-error
    # ele não exibe o corpo da resposta se o código HTTP for 400 ou mais
    - wget --content-on-error -qO- ${URL} --post-data ${POST_DATA}

Nesta solução, fui um pouco mais longe e decidi usar um único template CI para projetos Java.

Mais detalhes

Criei um projeto separado gitlab-ci onde coloquei o template CI para projetos Java common.yml.

common.yml

estágios:
  - construir
  - testar
  - verificar
  - implantar

variáveis:
  SONAR_ARGS: "
  -Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA} 
  -Dsonar.gitlab.ref_name=${CI_COMMIT_REF_NAME} 
  "

.build_java_project:
  estágio: construir
  tags:
    - touchbit-shell
  variáveis:
    SKIP_TEST: "false"
  script:
    - mvn clean
    - mvn package -DskipTests=${SKIP_TEST}
  artefatos:
    quando: sempre
    expirar_em: 30 dias
    caminhos:
      - "*\/target\/reports"

.build_sphinx_doc:
  estágio: construir
  tags:
    - touchbit-shell
  variáveis:
    DOCKERFILE: .indirect\/docs\/Dockerfile
  script:
    - docker build --no-cache -t ${CI_PROJECT_NAME}\/doc -f ${DOCKERFILE} .

.junit_module_test_run:
  estágio: testar
  tags:
    - touchbit-shell
  variáveis:
    MÓDULO: ""
  script:
    - cd ${MÓDULO}
    - mvn test
  artefatos:
    quando: sempre
    expirar_em: 30 dias
    caminhos:
      - "*\/target\/reports"

.junit_test_run:
  estágio: testar
  tags:
    - touchbit-shell
  script:
    - mvn test
  artefatos:
    quando: sempre
    expirar_em: 30 dias
    caminhos:
    - "*\/target\/reports"

.sonar_review:
  estágio: verificar
  tags:
    - touchbit-shell
  dependências: []
  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:
  estágio: implantar
  tags:
    - touchbit-shell
  variáveis:
    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:
  estende: .trigger_deploy
  apenas:
    - tags

.trigger_snapshot_deploy:
  estende: .trigger_deploy
  quando: manual
  exceto:
    - tags

Como resultado, os próprios projetos Java .gitlab-ci.yml aparecem bastante compactos e sucintos.

.gitlab-ci.yml

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

Shields4J:
  estende: .build_java_project

Sphinx doc:
  estende: .build_sphinx_doc
  variáveis:
    DOCKERFILE: .docs\/Dockerfile

Sonar review:
  estende: .sonar_review
  dependências:
    - Shields4J

Release:
  estende: .trigger_release_deploy

Snapshot:
  estende: .trigger_snapshot_deploy

Para o conteúdo

Configuração do pom.xml

Esta questão é descrita em detalhes. Googolplex em Configuração do Maven para assinatura automática e upload de artefatos em repositórios snapshot e staging., portanto, vou descrever algumas nuances do uso de plugins. Também descreverei como é fácil e natural usá-los. nexus-staging-maven-plugin, caso você não queira ou não possa usar org.sonatype.oss:oss-parent como pai do seu projeto.

maven-install-plugin

Instala os módulos no repositório local.
Muito útil para verificar soluções em outros projetos, assim como para verificação de somas de controle.

org.apache.maven.plugins
  maven-install-plugin
  
    
      install-project
      
      install
      
      
        target/${project.artifactId}-${project.version}.jar
```target/${project.artifactId}-${project.version}-sources.jar
        dependency-reduced-pom.xml
        
        true
        
        true

Para o conteúdo

maven-javadoc-plugin

Geração de javadoc para o projeto.

org.apache.maven.plugins
  maven-javadoc-plugin
  
    
      
        jar
      
      
      prepare-package
      
        
        true
        true
        
        false

Se você tem um módulo que não contém java (por exemplo, apenas recursos)
Ou se você não deseja gerar javadoc de forma alguma, isso pode ajudar maven-jar-plugin

org.apache.maven.plugins
  maven-jar-plugin
  
    
      empty-javadoc-jar
      generate-resources
      
        jar
      
      
        javadoc
        ${basedir}/javadoc

Para o conteúdo

maven-gpg-plugin

org.apache.maven.plugins
  maven-gpg-plugin
  
    
      sign-artifacts
      
      
      deploy
      
        sign

Para o conteúdo

nexus-staging-maven-plugin

Configuração:


  
    
      
      
        org.sonatype.plugins
        nexus-staging-maven-plugin
      
    
    
      
        
          org.sonatype.plugins
          nexus-staging-maven-plugin
          true
          
            sonatype
            https://oss.sonatype.org/
            
            
            true
          
        
        
          org.apache.maven.plugins
          maven-deploy-plugin
          
            
            true
          
        
      
    
  
  
    
      sonatype
      Repositório Nexus Snapshot
      https://oss.sonatype.org/content/repositories/snapshots/
    
    
      sonatype
      Repositório Nexus Release
      https://oss.sonatype.org/service/local/staging/deploy/maven2/

Se você tem um projeto multimódulo e não precisa enviar um módulo específico para o repositório, então no pom.xml desse módulo é necessário adicionar nexus-staging-maven-plugin com a flag skipNexusStagingDeployMojo

org.sonatype.plugins
      nexus-staging-maven-plugin
      
        true

Após o upload, as versões snapshot/release estarão disponíveis em repositórios de staging

SonatypeNexus
    https://oss.sonatype.org/content/groups/staging/
    

Mais benefícios

  • Uma lista muito rica de metas para trabalhar com o repositório nexus (mvn help:describe -Dplugin=org.sonatype.plugins:nexus-staging-maven-plugin).
  • Verificação automática de release para upload no maven central

Para o conteúdo

Resultado

Publicação da versão SNAPSHOT

Ao compilar o projeto, há a possibilidade de iniciar manualmente a tarefa de upload da versão SNAPSHOT para o nexus

Configuração do GitLab CI para o upload de projeto Java no Maven Central

Ao executar esta tarefa, uma tarefa correspondente no projeto de deploy é acionada (exemplo).

Log reduzido

Executando com gitlab-runner 11.10.0 (3001a600)
  no Deploy runner JSKWyxUw
Usando executor Shell...
Executando em ih1174328.vds.myihor.ru...
Ignorando configuração do repositório Git
Ignorando checkout do Git
Ignorando configuração dos submódulos do Git
$ 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} .
Clonando para 'shields4j'...
$ git checkout ${DEPLOY_CI_COMMIT_SHA}
Nota: checando '850f86aa317194395c5387790da1350e437125a7'.
Você está em estado 'HEAD destacado'. Você pode olhar ao redor, fazer alterações experimentais e confirmá-las, e pode descartar quaisquer confirmações que você fizer neste estado sem impactar quaisquer branches ao realizar outro checkout.
Se você quiser criar um novo branch para reter as confirmações que você criou, pode fazê-lo (agora ou depois) usando -b com o comando checkout novamente. Exemplo:
  git checkout -b new_branch_name
HEAD agora está em 850f86a... pular teste de deploy test-core
$ for pom in $(find . -name pom.xml); do # comando de várias linhas colapsado
$ if [[ "${DEPLOY_CI_COMMIT_TAG}" != "" ]]; então # comando de várias linhas colapsado
[INFO] Escaneando por projetos...
[INFO] Inspecionando build com um total de 4 módulos...
[INFO] Instalando recursos de Staging do Nexus:
[INFO]   ... total de 4 execuções do maven-deploy-plugin substituídas pelo nexus-staging-maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] Ordem de Build do Reactor:
[INFO] 
[INFO] Shields4J                                                          [pom]
[INFO] test-core                                                          [jar]
[INFO] Cliente Shields4J                                                   [jar]
[INFO] Ouvinte TestNG                                                     [jar]
[INFO] 
[INFO] -----------------------------
[INFO] Construindo Shields4J 1.0.0                                           [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] --- versions-maven-plugin:2.5:set (default-cli) @ shields4j-parent ---
[INFO] Buscando raiz do agregador local...
[INFO] Raiz de agregação local: /home/gitlab-deployer/JSKWyxUw/0/TouchBIT/deploy/shields4j
[INFO] Processando mudança de org.touchbit.shields4j:shields4j-parent:1.0.0 -> 1.0.0-SNAPSHOT
[INFO] Processando org.touchbit.shields4j:shields4j-parent
[INFO]     Atualizando projeto org.touchbit.shields4j:shields4j-parent
[INFO]         de versão 1.0.0 para 1.0.0-SNAPSHOT
[INFO] 
[INFO] Processando org.touchbit.shields4j:client
[INFO]     Atualizando pai org.touchbit.shields4j:shields4j-parent
[INFO]         de versão 1.0.0 para 1.0.0-SNAPSHOT
[INFO]     Atualizando dependência org.touchbit.shields4j:test-core
[INFO]         de versão 1.0.0 para 1.0.0-SNAPSHOT
[INFO] 
[INFO] Processando org.touchbit.shields4j:test-core
[INFO]     Atualizando pai org.touchbit.shields4j:shields4j-parent
[INFO]         de versão 1.0.0 para 1.0.0-SNAPSHOT
[INFO] 
[INFO] Processando org.touchbit.shields4j:testng
[INFO]     Atualizando pai org.touchbit.shields4j:shields4j-parent
[INFO]         de versão 1.0.0 para 1.0.0-SNAPSHOT
[INFO]     Atualizando dependência org.touchbit.shields4j:client
[INFO]         de versão 1.0.0 para 1.0.0-SNAPSHOT
[INFO]     Atualizando dependência org.touchbit.shields4j:test-core
[INFO]         de versão 1.0.0 para 1.0.0-SNAPSHOT
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Resumo do Reactor:
[INFO] 
[INFO] Shields4J 1.0.0 .................................... SUCESSO [  0.992 s]
[INFO] test-core .......................................... IGNORADO
[INFO] Cliente Shields4J ................................... IGNORADO
[INFO] Ouvinte TestNG 1.0.0 .............................. IGNORADO
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCESSO
[INFO] ------------------------------------------------------------------------
[INFO] Tempo total: 2.483 s
[INFO] Terminado em: 2019-04-21T02:40:42+03:00
[INFO] ------------------------------------------------------------------------
$ mvn clean deploy -DskipTests=${SKIP_TESTS}
[INFO] Escaneando por projetos...
[INFO] Inspecionando build com um total de 4 módulos...
[INFO] Instalando recursos de Staging do Nexus:
[INFO]   ... total de 4 execuções do maven-deploy-plugin substituídas pelo nexus-staging-maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] Ordem de Build do Reactor:
[INFO] 
[INFO] Shields4J                                                          [pom]
[INFO] test-core                                                          [jar]
[INFO] Cliente Shields4J                                                   [jar]
[INFO] Ouvinte TestNG                                                     [jar]
[INFO] 
[INFO] -----------------------------
[INFO] Construindo Shields4J 1.0.0-SNAPSHOT                                  [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
...
DELETADO
...
[INFO]  * Depósito em massa de artefatos de snapshot reunidos localmente finalizado.
[INFO] Implantação remota finalizada com sucesso.
[INFO] ------------------------------------------------------------------------
[INFO] Resumo do Reactor:
[INFO] 
[INFO] Shields4J 1.0.0-SNAPSHOT ........................... SUCESSO [  2.375 s]
[INFO] test-core .......................................... SUCESSO [  3.929 s]
[INFO] Cliente Shields4J ................................... SUCESSO [  3.815 s]
[INFO] Ouvinte TestNG 1.0.0-SNAPSHOT ..................... SUCESSO [ 36.134 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCESSO
[INFO] ------------------------------------------------------------------------
[INFO] Tempo total: 47.629 s
[INFO] Terminado em: 2019-04-21T02:41:32+03:00
[INFO] ------------------------------------------------------------------------

Como resultado, a versão foi carregada no nexus 1.0.0-SNAPSHOT.

Todas as versões snapshot podem ser removidas do repositório no site oss.sonatype.org com sua conta.

Configuração do GitLab CI para o upload de projeto Java no Maven Central

Para o conteúdo

Publicação da versão release

Ao instalar a tag, a tarefa correspondente no projeto de deploy para carregar a versão final no nexus é acionada automaticamente (exemplo).

Configuração do GitLab CI para o upload de projeto Java no Maven Central

O mais interessante é que o fechamento do release no nexus é acionado automaticamente.

[INFO] Realizando staging remoto...
[INFO] 
[INFO]  * Staging remoto no perfil de staging ID "9043b43f77dcc9"
[INFO]  * Repositório de staging criado com ID "orgtouchbit-1037".
[INFO]  * Repositório de staging em https://oss.sonatype.org:443/service/local/staging/deployByRepositoryId/orgtouchbit-1037
[INFO]  * Carregando artefatos localmente staged para o perfil org.touchbit
[INFO]  * Upload dos artefatos localmente staged concluído.
[INFO]  * Fechando o repositório de staging com ID "orgtouchbit-1037".
Aguardando a conclusão da operação...
.........
[INFO] Repositórios remotamente staged 1, finalizado com sucesso.
[INFO] ------------------------------------------------------------------------
[INFO] Resumo do Reator:
[INFO] 
[INFO] Shields4J 1.0.0 .................................... SUCESSO [  9.603 s]
[INFO] test-core .......................................... SUCESSO [  3.419 s]
[INFO] Cliente Shields4J ................................... SUCESSO [  9.793 s]
[INFO] Ouvinte TestNG 1.0.0 .............................. SUCESSO [01:23 min]
[INFO] ------------------------------------------------------------------------
[INFO] CONSTRUÇÃO COM SUCESSO
[INFO] ------------------------------------------------------------------------
[INFO] Tempo total: 01:47 min
[INFO] Finalizado em: 2019-04-21T04:05:46+03:00
[INFO] ------------------------------------------------------------------------

E se algo der errado, a tarefa certamente falhará

[INFO] Realizando staging remoto...
[INFO] 
[INFO]  * Staging remoto no perfil de staging ID "9043b43f77dcc9"
[INFO]  * Repositório de staging criado com ID "orgtouchbit-1038".
[INFO]  * Repositório de staging em https://oss.sonatype.org:443/service/local/staging/deployByRepositoryId/orgtouchbit-1038
[INFO]  * Enviando artefatos localmente staged para o perfil org.touchbit
[INFO]  * Upload de artefatos localmente staged concluído.
[INFO]  * Fechando repositório de staging com ID "orgtouchbit-1038".
Aguardando a conclusão da operação...
.......
[ERROR] Falha na regra ao tentar fechar o repositório de staging com ID "orgtouchbit-1039".
[ERROR] 
[ERROR] Relatório de Falhas das Regras de Staging do Nexus
[ERROR] ==================================
[ERROR] 
[ERROR] Falhas no repositório "orgtouchbit-1039"
[ERROR]   Falhas na regra "signature-staging"
[ERROR]     * Nenhuma chave pública: A chave com id: (1f42b618d1cbe1b5) não pôde ser localizada em http://keys.gnupg.net:11371/. Faça o upload de sua chave pública e tente a operação novamente.
...
[ERROR] Limpando o diretório local de staging após uma falha de regra durante o fechamento dos repositórios de staging: [orgtouchbit-1039]
[ERROR]  * Deletando contexto 9043b43f77dcc9.properties
[ERROR] Limpando os repositórios remotos de staging após uma falha de regra durante o fechamento dos repositórios de staging: [orgtouchbit-1039]
[ERROR]  * Excluindo repositório de staging com falha ID "orgtouchbit-1039" (Falha de regra durante o fechamento dos repositórios de staging: [orgtouchbit-1039]).
[ERROR] Staging remoto finalizado com falha: Falha nas regras de staging!
[INFO] ------------------------------------------------------------------------
[INFO] Resumo do Reator:
[INFO] 
[INFO] Shields4J 1.0.0 .................................... SUCESSO [  4.073 s]
[INFO] test-core .......................................... SUCESSO [  2.788 s]
[INFO] Cliente Shields4J .................................. SUCESSO [  3.962 s]
[INFO] TestNG listener 1.0.0 .............................. FALHA [01:07 min]
[INFO] ------------------------------------------------------------------------
[INFO] FALHA NA COMPILAÇÃO
[INFO] ------------------------------------------------------------------------

Resulta que nos resta apenas uma escolha. Ou excluir esta versão ou publicá-la.

Configuração do GitLab CI para o upload de projeto Java no Maven Central

Após o lançamento, após algum tempo, os artefatos estarão em Configuração do GitLab CI para o upload de projeto Java no Maven Central

fora do tópico

Para mim, foi uma surpresa que o Maven indexa outros repositórios públicos.
Tive que adicionar um robots.txt, pois ele indexou meu repositório antigo.

Para o conteúdo

Conclusão

O que temos

  • Um projeto de deploy separado no qual várias tarefas de CI podem ser realizadas para carregar artefatos em repositórios públicos para diferentes linguagens de desenvolvimento.
  • O projeto de deploy está isolado de interferências externas e pode ser alterado apenas por usuários com o papel de Owner e Maintainer.
  • Um Runner específico separado com cache 'quente' para executar apenas tarefas de deploy.
  • Publicação de versões snapshot/release em repositórios públicos.
  • Verificação automática da versão release para pronta publicação no Maven Central.
  • Proteção contra a publicação automática de versões 'cruas' no Maven Central.
  • Compilação e publicação de versões snapshot 'com um clique'.
  • Um repositório único para obter versões snapshot/release.
  • Pipeline geral para construção/teste/publicação de projetos Java.

Configurar o GitLab CI não é tão complicado quanto parece à primeira vista. Basta configurar o CI 'pronto para uso' algumas vezes e você já não é mais um iniciante na área. Além disso, a documentação do GitLab é bastante abrangente. Não tenha medo de dar o primeiro passo. O caminho se forma sob os passos de quem avança (não me lembro quem disse isso 🙂 ).

Ficarei feliz em receber feedback.

No próximo artigo, vou explicar como configurar o GitLab CI para a execução concorrente de tarefas com testes de integração (iniciando serviços testados usando docker-compose), caso você tenha apenas um shell runner.

Para o conteúdo

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster