Cloner tous les repos d’une organisation sur Github

Je cherchais un moyen rapide de cloner tous les repos publics d’une organisation sur Github. D’autres se sont bien sûr posés la question mais les solutions proposées m’ont toutes semblées trop complexes / tordues.

Résultat, j’ai fait la mienne :

bash -x <(curl -s "https://api.github.com/orgs/alphagov/repos" | jq -r '.[].git_url | "git clone " + sub("git://"; "https://")')

L’organisation est ici alphagov, à remplacer évidemment.

Il y a trois étapes :

  1. curl -s "https://api.github.com/orgs/alphagov/repos" récupère la liste des repos de l’organisation au format JSON. Il y a pas mal d’informations dedans (toutes les URLs, nombre de forks, de watchers, etc.).
  2. On passe à jq, un outil (très puissant) en ligne de commande pour parser du JSON qui a son propre langage.
    1. .[].git_url indique de conserver uniquement l’URL du repo. Ce qui donne :
      $ curl -s "https://api.github.com/orgs/alphagov/repos" | jq -r '.[].git_url'
      git://github.com/alphagov/static.git
      git://github.com/alphagov/slimmer.git
      (...)
      
    2. sub("git://"; "https://") permet ainsi de remplacer les git:// par des https:// car je ne suis pas authentifié sur Github. Ce qui donne :
      $ curl -s "https://api.github.com/orgs/alphagov/repos" | jq -r '.[].git_url | "git clone " + sub("git://"; "https://")'
      git clone https://github.com/alphagov/static.git
      git clone https://github.com/alphagov/slimmer.git
      (...)
      
  3. Le tout est envoyé en entrée à bash qui va exécuter les commandes et cloner tous les répertoires !

Bonus : Pourquoi alphagov ? Car c’est une très bonne source d’inspiration tant en Ruby (la plupart sont des applications Rails) qu’en Puppet et en Terraform (ici et ici).

Nettoyer les sessions Gitlab < 7.3 dans la base Redis

Prior to GitLab 7.3, user sessions did not automatically expire from Redis. If you have been running a large GitLab server (thousands of users) since before GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis database after you upgrade to GitLab 7.3.

Les versions de Gitlab entre la 6.2 et la 7.2 stockent leurs sessions dans une base Redis sans définir de date d’expiration. Conséquence : celles-ci vont s’accumuler au fil du temps sans aucun intérêt.

La documentation de Gitlab donne deux commandes :

# Voir toutes les sessions stockées dans Redis
redis-cli keys '*' | grep '^[a-f0-9]\{32\}$' | wc -l

# Faire expirer toutes les sessions dans dix minutes
redis-cli keys '*' | grep '^[a-f0-9]\{32\}$' | awk '{ print "expire", $0, 600 }' | redis-cli

La commande ci-dessus pose un problème : à chaque exécution, il repositionne la date d’expiration à 600 secondes. Et ce, même si la session allait expirée avant ! Impossible donc de l’exécuter telle quelle via une tâche cron.

Voici un script shell conçu pour être exécuté via cron. Il s’inspire de celui la documentation mais ignore toutes les sessions qui ont déjà une date d’expiration et définit pour les autres une échéance à +1 heure :

#!/bin/sh
#
# Avant Gitlab 7.3, les sessions ne possèdent pas de date d'expiration.
#
# Cela a pour conséquence une augmentation progressive de la base Redis
# sans aucune plus-value.
#
# Ce script se charge de définir une date d'expiration pour toutes les sessions
# dont le nom est formaté sous la forme de 32 caractères hexadécimales
#
# Le script fournit sur la documentation de Gitlab propose une expiration d'un quart
# d'heure, ce qui me semble trop court, le délai ici est donc d'une heure.
#
# Source :
# https://docs.gitlab.com/ce/administration/operations/cleaning_up_redis_sessions.sh
#

# Durée d'une session en secondes
SESSION_EXPIRE="3600"

rcli="$(command -v redis-cli)"

# Pour voir le nombre de sessions dans la base
# $rcli keys '*' | grep '^[a-f0-9]\{32\}$' | wc -l

# Expiration au bout d'une heure - 3600 secondes
$rcli keys '*' | grep '^[a-f0-9]\{32\}$' | while read LINE
do
  # Identifiant de la session
  key_id="$(echo $LINE | awk '{ print $0 }')"

  # Récupération de la date d'échéance correspondante
  key_ttl="$($rcli ttl $key_id)"

  # On ne prend que les sessions dont le TTL est -1 (= pas d'expiration)
  test "${key_ttl}" = -1 || continue

  echo "$key_id = $key_ttl"

  # Définit une date d'expiration
  $rcli expire "${key_id}" "${SESSION_EXPIRE}"
done

L’idéal étant évidemment de passer à une version plus récente de Gitlab 🙂

Utiliser sudo avec un pipe ou une redirection

Comment exécuter via sudo une commande qui comprend un pipe (|) ou une redirection (>) ?

La seule solution universelle (comprendre, ne dépendant pas d’un shell en particulier) passe par la commande sh à laquelle il est possible de passer les instructions directement en paramètres (à l’aide de l’option -c):

sudo sh -c 'echo "nameserver 8.8.8.8" >> /etc/resolv.conf'

Ici, nameserver 8.8.8.8 va être ajouté à la fin de /etc/resolv.conf à travers une redirection.

Cette méthode pose néanmoins un problème: toutes les instructions / commandes passées entre guillemets vont être exécutés avec les droits root (MAUVAIS !).

Il est donc préférable, lorsque cela est possible, de n’utiliser sudo que pour la commande le nécessitant:

curl 'http://download.mono-project.com/repo/xamarin.gpg' | sudo apt-key add -

Ici, curl est exécuté avec les droits normaux de l’utilisateur courant, sa sortie étant envoyée à apt-key qui, lui, s’exécutera avec les droits super-administrateurs.