Utiliser Gitlab Pages comme repository Debian

Les Gitlab Pages (et Github Pages) permettent via un processus de construction d'artefact de publier un site web au travers d'un pipeline.

Un repository Debian étant simplement constitué de fichiers statiques, nous pouvons donc faire un rapprochement entre les pipelines de build et un repository Debian.

Pour réaliser notre repository Debian hébergé sur des gitlab pages nous aurons besoin:

  • Du conteneur docker debian:stretch
  • De GPG pour la signature du repository
  • De reprepro pour créer notre repository
  • D'un repository git hébergé sur un Gitlab

Initialisation du repository

Créer un répertoire sur votre machine et initialisez le lien avec Gitlab:

mkdir debianrepo
cd debianrepo
git init
git remote add origin git@gitlab.com:nerzhul/debianrepo.git

Génération de la clef GPG

Dans un premier temps nous aurons besoin d'une clef GPG pour notre repository. Générez une clef:

gpg --gen-key

On va ensuite exporter la clef publique et la clef privée dans notre repository local:

mkdir repo_meta
gpg --armor --output repo_meta/gpg-public.key --export BDB31B849949AD389C76573EF02015F1E3A69CF2
gpg --export-secret-keys -a BDB31B849949AD389C76573EF02015F1E3A69CF2 > repo_meta/gpg-private.key

Note: exposer la clef privée dans le repository n'est pas recommandé, il est plus judicieux d'utiliser les secret variables de Gitlab pour stocker le contenu de la clef privée. Il s'agit juste de simplifier l'exercice ici

Création du fichier de définition du repository

Créez un fichier repo_distributions dans le répertoire repo_meta. Celui-ci nous servira plus tard, lors de la création du repository. Notez bien que la ligne SignWith contient le fingerprint de la clef GPG précédemment créée

Origin: Debian
Label: Debian
Codename: stretch
Architectures: amd64
Components: main
Description: Apt repository for the posterity
SignWith: BDB31B849949AD389C76573EF02015F1E3A69CF2

Création du pipeline de construction du repository

Le pipeline de repository s'appuie sur le fichier .gitlab-ci.yml de Gitlab. Voici le fichier que nous allons utiliser, nous le détaillerons juste après:

---
image: debian:stretch

stages:
  - package
  - deploy

variables:
  DEBIAN_VERSION: "stretch"
  ETCD_VERSION: 3.2.7
  RUNDECK_VERSION: 2.9.3-1

package:etcd:
  stage: package
  before_script:
    - find etcd -type d -exec chmod 755 {} \;
    - find etcd -type f -not -name postinst -exec chmod 644 {} \;
    - find etcd -type f -name postinst -exec chmod 755 {} \;
    - mkdir -p build/ dist/
    - apt-get -qy update > /dev/null
    - apt-get -qyy install wget > /dev/null
  script:
    - sed -i 's/%%ETCD_VERSION%%/'${ETCD_VERSION}'/g' etcd/DEBIAN/control
    - mkdir -p etcd/usr/bin/
    - cd build/
    - wget https://github.com/coreos/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
    - tar xzf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
    - cp etcd-v${ETCD_VERSION}-linux-amd64/etcd etcd-v${ETCD_VERSION}-linux-amd64/etcdctl ../etcd/usr/bin/
    - cd ..
    - dpkg-deb -b etcd/
    - mv etcd.deb dist/
  artifacts:
    when: on_success
    expire_in: 1 week
    paths:
      - dist/*

package:rundeck:
  stage: package
  before_script:
    - apt-get -qy update > /dev/null
    - apt-get -qyy install wget > /dev/null
    - mkdir dist/
  script:
    - cd dist/
    - wget http://dl.bintray.com/rundeck/rundeck-deb/rundeck-${RUNDECK_VERSION}-GA.deb
  artifacts:
    when: on_success
    expire_in: 1 week
    paths:
      - dist/*

pages:
  stage: deploy
  only:
    - master
  dependencies:
    - package:etcd
    - package:rundeck
  before_script:
    - apt-get -qy update > /dev/null
    - apt-get -qyy install reprepro > /dev/null
    - gpg --import repo_meta/gpg-private.key
  script:
    - mkdir -p public/conf
    - cp repo_meta/repo_distributions public/conf/distributions
    - cp repo_meta/gpg-public.key public/repo.key
    - cd public
    - reprepro includedeb ${DEBIAN_VERSION} ../dist/*.deb
  artifacts:
    when: on_success
    expire_in: 1 year
    paths:
      - public

Notre pipeline se décompose en 2 phases:

  • Construction des paquets du repository
  • Création et déploiement du repository

La phase de déploiement nécessite de connaître les paquets Debian construits. Par convention les paquets Debian construits seront placés dans le répertoire dist/ qui sera transféré à la phase de déploiement des Gitlab Pages.

Nous avons ici 2 paquets différents, Etcd et Rundeck qui sont 2 outils libres. Le premier ne fournit pas de paquet Debian, le second oui.

Création des paquets

Commençons par Rundeck, le plus simple.

package:rundeck:
  stage: package
  before_script:
    - apt-get -qy update > /dev/null
    - apt-get -qyy install wget > /dev/null
    - mkdir dist/
  script:
    - cd dist/
    - wget http://dl.bintray.com/rundeck/rundeck-deb/rundeck-${RUNDECK_VERSION}-GA.deb
  artifacts:
    when: on_success
    expire_in: 1 week
    paths:
      - dist/*

Dans la section __before_script__, nous mettons ce qui ne concerne pas le build, ici l'installation de wget pour télécharger le paquet.

Nous nous plaçons ensuite dans le répertoire dist et téléchargeons la version de Rundeck spécifiée dans les variables du CI, plus haut.

Les artefacts intermédiaires sont ensuite sauvegardés pour 1 semaine. Pour rappel on peut télécharger les artefacts depuis l'interface de Gitlab sur chaque pipeline.

Passons maintenant au paquet etcd:

package:etcd:
  stage: package
  before_script:
    - find etcd -type d -exec chmod 755 {} \;
    - find etcd -type f -not -name postinst -exec chmod 644 {} \;
    - find etcd -type f -name postinst -exec chmod 755 {} \;
    - mkdir -p build/ dist/
    - apt-get -qy update > /dev/null
    - apt-get -qyy install wget > /dev/null
  script:
    - sed -i 's/%%ETCD_VERSION%%/'${ETCD_VERSION}'/g' etcd/DEBIAN/control
    - mkdir -p etcd/usr/bin/
    - cd build/
    - wget https://github.com/coreos/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
    - tar xzf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
    - cp etcd-v${ETCD_VERSION}-linux-amd64/etcd etcd-v${ETCD_VERSION}-linux-amd64/etcdctl ../etcd/usr/bin/
    - cd ..
    - dpkg-deb -b etcd/
    - mv etcd.deb dist/
  artifacts:
    when: on_success
    expire_in: 1 week
    paths:
      - dist/*

Pour etcd nous construisons un paquet Debian depuis des binaires fournis par les développeurs. Le répertoire etcd contient l'arborescence minimale du paquet, à savoir la configuration, l'unit system et les fichiers control et postinst de Debian.

Dans l'étape __before_script__ nous avons également besoin de wget, mais également de corriger les permissions sur le répertoire etcd du repository, le Gitlab CI changeant les droits sur les fichiers. Je n'ai pas réussi à comprendre pourquoi, celles-ci sont correctes dans git.

L'étape de build se décompose ensuite de la manière suivante:

  • On modifie le fichier control du paquet afin de spécifier la version du paquet etcd
  • On télécharge l'archive etcd du release concerné, qu'on extrait
  • On copie les binaires nous intéressant dans le paquet dans /usr/bin
  • On construit le paquet avec dpkg-deb
  • On bouge le paquet dans le répertoire dist

Enfin, tout comme pour Rundeck, on archive l'artefact.

Création du repository Debian et publication

Notre dernière tâche consiste à créer le repository Debian pour l'exposer via les Gitlab Pages

pages:
  stage: deploy
  only:
    - master
  dependencies:
    - package:etcd
    - package:rundeck
  before_script:
    - apt-get -qy update > /dev/null
    - apt-get -qyy install reprepro > /dev/null
    - gpg --import repo_meta/gpg-private.key
  script:
    - mkdir -p public/conf
    - cp repo_meta/repo_distributions public/conf/distributions
    - cp repo_meta/gpg-public.key public/repo.key
    - cd public
    - reprepro includedeb ${DEBIAN_VERSION} ../dist/*.deb
  artifacts:
    when: on_success
    expire_in: 1 year
    paths:
      - public

L'étape du pipeline doit obligatoirement se nommer pages par convention de Gitlab et le livrable du repository doit se situer dans le répertoire public.

Nous ajoutons nos deux étapes de construction de paquets en dépendances, afin d'avoir les artefacts à disposition pour cette étape, puis nous installons reprepro qui va nous permettre de construire un repository Debian facilement, et nous importons la clef GPG privées dans le trousseau du conteneur de build.

La construction du repository se fait ensuite de la manière suivante:

  • Copie du fichier repo_meta/repo_distributions vers public/conf/distributions, utilisé par reprepro pour construire le repository
  • Copie de la clef GPG publique vers public/repo.key afin de la distribuer à nos clients
  • En se positionnant dans le répertoire public, inclusion de l'ensemble des fichiers deb du répertoire dist/ dans le repository

Enfin nous archivons le repository pour une durée de 1 an, afin d'assurer sa pérennité dans le temps.

Vous pouvez maintenant commiter et pousser tous les changements

Résultat

Allez maintenant sur votre repository Gitlab dans la section pipelines, vous devriez avoir un résultat similaire:

Vous savez désormais construire une repository Debian en ajoutant dynamiquement des paquets construits à partir d'un pipeline Gitlab. Je vous invite à consulter le repository ayant servi de PoC puis de référence pour cet article:

Gitlab CI: Pipeline maven

Le Gitlab CI intègre depuis la version 8.8 de Gitlab la notion de pipeline. C'est une notion très à la mode permettant de pouvoir séparer son processus de build en plusieurs étapes distinctes, interdépendantes et parallélisables. Nous allons prendre ici l'exemple d'une application SpringBoot utilisant Maven et créer un pipeline de construction de l'application ayant le cheminement suivant

  • Construction de l'application
  • Tests unitaires
  • Tests de qualité de code (sonar)
  • Déploiement Nexus (SNAPSHOT)
  • Déploiement Nexus (release, branche master uniquement)

Préparation des Gitlab Runner (builders)

Dans un premier temps nous allons créer 2 répertoires partagés par nos runners pour nos builds maven

  • un répertoire .m2 qui contiendra la configuration maven standard pour nos builds
  • un répertoire tools qui contiendra notre script de release d'application Maven

mkdir -p /var/lib/gitlab-runner/.m2 /var/lib/gitlab-runner/tools On configure ensuite le runner gitlab pour utiliser en read-only le .m2 et les outils. Editez le fichier /etc/gitlab-runner/config.toml:

concurrent = 2
check_interval = 0
[[runners]]
  name = "runner01"
  url = "http://gitlab.example.org/"
  token = "99aab9a13c8efda114771f017fcf"
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "debian:jessie"
    privileged = false
    disable_cache = true
    volumes = ["/cache", "/var/lib/gitlab-runner/.m2:/.m2:ro", "/var/lib/gitlab-runner/tools:/tools:ro"]
  [runners.cache]

Notre runner est pratiquement prêt, avant de le finir nous allons configurer le pipeline Maven afin que vous compreniez mieux la dernière phase. Vous pouvez voir ici 2 choses sortant du commun:

  • un répertoire /.m2 provenant de l'hôte monté en read-only. Il portera notre configuration Maven (.m2/settings.xml), avec notre URL Nexus
  • un répertoire /tools lui aussi en read-only. Il contiendra certains scripts particuliers, notamment ici notre script de release Maven et la clef SSH privée permettant d'accéder en écriture au repository

Configuration du pipeline Maven

Notre pipeline maven s'appuie sur 5 étapes:

  • Construction de l'artefact
  • Tests
  • Déploiement dans Nexus
  • Génération de release
  • Documentation

Les étapes de documentation, déploiement Nexus et de génération de release ne seront effectuées que dans certaines conditions:

  • Déploiement dans Nexus: si nous construisons un snapshot, donc, ni sur un tag git, ni sur une branche nommée releases.
  • Génération de release: uniquement lors d'un commit sur la branche releases.
  • Documentation: uniquement lors d'un tag git

Afin de s'y retrouver, je vous propose de regarder le graphe suivant Notre étape de tests est découpée en 2 tâches parallèles:

  • Tests unitaires et d'intégration
  • Tests sonar

Passons maintenant au fichier .gitlab-ci.yml que nous allons ajouter au repository:

---
image: maven:3-jdk-8
cache:
 paths:
 - $HOME/.m2/
variables:
  MAVEN_CLI_OPTS: "-s /.m2/settings.xml -B"
  SONAR_PROJECT_NAME: "project-demo"
stages:
- build
- test
- deploy
- documentation
- release

maven:build:
stage: build
environment: staging
script:
- "mvn $MAVEN_CLI_OPTS clean compile"
artifacts:
when: on_success
expire_in: 1 day
paths:
- target/

maven:package:
stage: test
environment: staging
dependencies:
- maven:build
script: "mvn $MAVEN_CLI_OPTS package"
artifacts:
when: on_success
expire_in: 1 day
paths:
- target/

maven:sonar:
stage: test
environment: staging
dependencies:
- maven:build
script: "mvn $MAVEN_CLI_OPTS verify sonar:sonar -D sonar.projectName=$SONAR_PROJECT_NAME -Dsonar.gitlab.commit_sha=$CI_BUILD_REF -Dsonar.gitlab.ref_name=$CI_BUILD_REF_NAME"
artifacts:
when: on_success
expire_in: 1 day
paths:
- target/

maven:deploy-nexus:
stage: deploy
environment: staging
except:
- tags
- releases
dependencies:
- maven:package
- maven:sonar
script: "mvn $MAVEN_CLI_OPTS deploy"
artifacts:
when: on_success
expire_in: 1 day
paths:
- target/*.jar

maven:javadoc:
stage: documentation
environment: staging
only:
- tags
dependencies:
- maven:deploy-nexus
script: "mvn $MAVEN_CLI_OPTS javadoc:javadoc"
artifacts:
when: on_success
expire_in: 1 week
paths:
- target/site/apidocs/

maven:release:
stage: release
environment: staging
only:
- releases
dependencies:
- maven:deploy-nexus
script:
- "/tools/maven-release"

Nous utilisons ici une image docker officielle contenant un builder Maven 3.3 et une JVM Java 8. Gitlab 8.16 et inférieurs ne permettant pas de pousser depuis le CI nous allons devoir ruser en utilisant un script avec une clef privée spécifique d'édition des repositories. Je reviendrai plus tard sur la méthode pour Gitlab 9.0 et supérieurs. Je vous propose de regarder le script ci-dessous:

#! /bin/bash
which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y ) > /dev/null

# Run ssh-agent (inside the build environment)
eval $(ssh-agent -s) > /dev/null

# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
ssh-add /tools/.ssh/id_rsa > /dev/null
mkdir -p ~/.ssh
ssh-keyscan gitlab.example.org > ~/.ssh/known_hosts 2>/dev/null
[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config

git config --global user.email 'gitlab-ci@example.org'
git config --global user.name 'Gitlab CI'

# Reconfigure push repository to use SSH
CI_PUSH_REPO=`echo $CI_BUILD_REPO | perl -pe 's#.*@(.+?(\:\d+)?)/#git@\1:#'`
git remote set-url --push origin "${CI_PUSH_REPO}"

# Maven doesn't like detach HEADs, attach
git checkout -B "$CI_BUILD_REF_NAME"
mvn $MAVEN_CLI_OPTS release:clean -DskipTests=true
mvn $MAVEN_CLI_OPTS release:prepare -DskipTests=true

# prepare done, pushing tags
git push origin --tags
mvn $MAVEN_CLI_OPTS release:perform -DskipTests=true

Il s'agit ici de résoudre 2 problématiques:

  • concilier notre problème de push, en lisant la clef SSH privée de notre repository, en l'ajoutant à l'agent SSH du runner et en configurant notre commiter git.
  • ré-attacher le commit du build à sa branche, sinon Maven ne fonctionnera pas lors du release.

Enfin on pousse les tags générés par Maven et on pousse l'artefact vers Nexus. Le push du tag va déclencher un second pipeline générant automatiquement la documentation.

Conclusion

Nous avons vu ici comment utiliser efficacement le Gitlab CI pour construire une application standard Maven depuis Gitlab. Si vous utilisez Jenkins, Gitlab CI offre une alternative intéressante, notamment par des pipelines extrêmement flexibles, vous permettant de construire facilement des applications au moyen de conteneurs Docker, et donc reproductibles, avec des workspaces propres. A vous de jouer !

Gitlab: réinitialiser le mot de passe admin

Si vous avez installé depuis gitlab vous pouvez avoir perdu le mot de passe admin, ne vous connectant qu'en utilisateur non privilégié. Si vous n'avez pas changé l'adresse mail de l'administrateur il vous est donc impossible de demander une réinitialisation de mot de passe.

Pas de panique, une solution existe pour pallier à ce problème.

Connectez vous en SSH sur le serveur gitlab, puis appelez la console gitlab de la manière suivante

gitlab-rails console production

Vous devriez voir apparaître un prompt ruby:

irb(main):001:0>

Tapez ensuite la commande suivante afin de trouver l'utilisateur administrateur:

u = User.where(id: 1).first

Vous devriez voir apparaître une entrée avec le login, le mot de passe chiffré et l'adresse e-mail de l'administrateur.

Pour réinitialiser le mot de passe, tapez la suite de commandes suivantes:

u.password = 'mdpquejeretiendraicettefois'
u.password_confirmation = 'mdpquejeretiendraicettefois'
u.save!

N'oubliez pas le !, auquel cas la modification ne sera pas sauvegardée.

Vous avez désormais réinitialisé votre mot de passe administrateur ! Bien sûr la commande est valable pour tout utilisateur local bloqué.

Installer Gitlab sous FreeBSD

Introduction

Gitlab est un puissant outil permettant de monter une infrastructure de type github sur son infrastructure. Gitlab peut se connecter à un annuaire LDAP d'entreprise afin que les utilisateurs soient automatiquement reconnus. En terme de fonctionnalités, Gitlab permet de créer des repositories git, forker, créer des branches, faire des commits sur HTTP(S), partager des projets entre plusieurs utilisateurs... Nous allons ici voir comment installer Gitlab sur un serveur FreeBSD.

Prérequis

Afin d'utiliser Gitlab il vous faudra installer les packages suivants:

pkg install git icu libxml2 libxslt postfix python27 logrotate py27-docutils rubygem-bundler rubygem-passenger sudo gmake rubygem-rake

Bases de données

Afin d'utiliser Gitlab il vous faudra deux bases de données, une base Redis et une base MySQL/PostgreSQL. Nous utiliserons ici une base PostgreSQL 9.2 existante. Vous pouvez installer PostgreSQL et Redis sur le serveur, néanmoins nous utiliserons un serveur externe.

Installation et configuration de Redis

On installe le paquet Redis, on active le service et on le lance:

pkg install redis
echo "redis_enable=\"yes\"" > /etc/rc.confservice redis start

Création de la base et de l'utilisateur PostgreSQL

Le service de base de données PostgreSQL est considéré comme prêt, on va ajouter une base de données et un utilisateur:

su - pgsql
createdb gitlab
psql gitlab
CREATE USER gitlab WITH PASSWORD 'dbpwd';
GRANT ALL PRIVILEGES ON DATABASE gitlab TO gitlab;
\q

Préparation de l'environnement

Nous aurons besoin d'un utilisateur et d'un groupe dédié:

pw addgroup git
pw adduser git -g git -m -d /usr/local/www/gitlab -c "GitLab"
ln -s /usr/local/bin/git /usr/bin/git

Gitlab-shell

Installation

On va installer le shell gitlab, permettant d'interface gitlab avec git. On se connecte en utilisateur git et on clone les sources:

su - git
git clone https://github.com/gitlabhq/gitlab-shell.git
git checkout v1.9.4

Configuration

Toujours en utilisateur git, on copie la configuration d'example

cp config.yml.example config.yml

Il faut modifier ici trois éléments:

  • Changez tout d'abord l'URL de votre site (gitlab_url)
  • Remplacez les chemins en /home/git par /usr/local/www/gitlab
  • Configurez redis en paramétrant le chemin du binaire (/usr/local/bin/redis-cli) et l'adresse de votre serveur Redis

Enfin lancez la commande /usr/local/www/gitlab/gitlab-shell/bin/install.

Gitlab

Installation

En utilisateur git on clone le repository gitlab et on se place sur la version en cours

su - git
git clone https://github.com/gitlabhq/gitlabhq.git gitlab
git checkout 6-8-stable

Configuration

En utilisateur git, copiez la configuration de gitlab et éditez la

cp config/gitlab.yml.example config/gitlab.yml

Tout d'abord modifiez le nom du serveur web, l'hôte et le port:

host: gitlab.unix-experience.fr
port: 443
https: true

Ensuite l'adresse mail de notification et de support:

email_from: gitlab@unix-experience.fr
support_email: support@unix-experience.fr

Modifiez le chemin de git:

bin_path: /usr/local/bin/git

Modifiez tous les chemins contenant /home/git par /usr/local/www/gitlab/ Copiez ensuite la configuration d'unicorn et modifiez également les chemins. *

cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb

Ajoutez également la ligne listen suivante:

listen "/usr/local/www/gitlab/gitlab/tmp/sockets/gitlab.socket"

Enfin copiez et modifiez la configuration de base de données:

cp config/database.yml.postgresql config/database.yml
vi config/database.yml

On va ensuite procéder à l'installation d'une gem et de notre base de données.

gem install charlock_holmes --version '0.6.9.4'
bundle install --deployment --without development test mysql aws

Enfin, en utilisateur git, toujours dans le répertoire d'installation de gitlab, on termine l'installation en préparant les assets.

bundle exec rake assets:precompile RAILS_ENV=production

Script init

Gitlab utilise un service qui s'exécute sur la machine. On va copier le script init dans le rc.d de FreeBSD et le lancer

cp lib/support/init.d/gitlab /usr/local/etc/rc.d/gitlab
chmod +x /usr/local/etc/rc.d/gitlab
service gitlab start

Configuration Apache

Après avoir installé rubygem-passenger (dans la liste de prérequis), vous devrez compiler le module pour Apache. Lancez simplement la commande suivante:

/usr/local/lib/ruby/gems/1.9/gems/passenger-4.0.41/bin/passenger-install-apache2-module

Sélectionnez ruby et validez, puis attendez que la compilation se termine. Ajoutez ensuite les lignes suivantes dans votre configuration Apache:

LoadModule passenger_module /usr/local/lib/ruby/gems/1.9/gems/passenger-4.0.41/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
        PassengerRoot /usr/local/lib/ruby/gems/1.9/gems/passenger-4.0.41
        PassengerDefaultRuby /usr/local/bin/ruby19
</IfModule

Workaround: Problème avec la librairie underscore

Si votre Gitlab rencontre un erreur (en 6.8 c'est le cas), lorsque vous vous connectez en interface web, vérifiez les logs de l'application dans /usr/local/www/gitlab/gitlab/log/production.log. Si vous rencontrez l'erreur suivante:

couldn't find file 'underscore'

Editez le fichier /usr/local/www/gitlab/gitlab/app/assets/javascripts/application.js.coffee et supprimez la ligne concernant underscore

Conclusion

Vous avez désormais en votre possession un Gitlab opérationnel, permettant d'ajouter une valeur ajoutée à votre entité, en offrant un service de qualité de type github, tout en gardant la main-mise sur les données.

Source

Afin de rédiger cet article je me suis basé sur le tutoriel suivant et l'ai modifié afin de moderniser l'installation de gitlab. https://github.com/chadliu23/Installation-guide-for-GitLab6-on-Freebsd