[RUBY] Une histoire qui a rendu aussi facile que possible la vérification de l'opération lors de la création automatique d'une mise à jour de bibliothèque PR avec Github Dependabot

Qu'est-ce que Dependabot en premier lieu?

Github a une fonction appelée Dependabot Saviez-vous qu'il y en a? S'il existe une ancienne dépendance (bibliothèque) utilisée dans le référentiel, c'est une fonction qui émet automatiquement une demande d'extraction mise à jour. Beaucoup d'entre vous ont peut-être vu le PR suivant dans des référentiels publics.

スクリーンショット 2020-09-09 20.12.31.png

À l'origine un service indépendant, il a été acquis par Github et incorporé en tant que fonctionnalité native, ce qui facilite son déploiement. Vous pouvez activer la création automatique de la mise à jour de la bibliothèque PR en plaçant simplement le fichier de configuration sous .github / dependabot.yml.

Je vais omettre l'explication de Dependabot lui-même cette fois, mais dans le projet dans lequel je suis impliqué, j'ai commencé l'opération afin que le fichier de configuration suivant soit vérifié à 9 heures lundi.

version: 2
updates:
  - package-ecosystem: "gradle"
    directory: "/"
    target-branch: "develop"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "Asia/Tokyo"
    reviewers:
      - "ignis-ltd/with-android"

Tout d'abord, si vous souhaitez en savoir plus sur Github Dependabot, les documents et articles officiels suivants vous seront utiles.

Problèmes opérationnels Dependabot

Dependabot recevra un PR pour chaque mise à jour de bibliothèque. Étant donné que certaines mises à jour de la bibliothèque peuvent causer des problèmes, je pense que c'est la bonne forme à individualiser pour isoler le problème, mais s'il y a plusieurs mises à jour, certains problèmes subsisteront. ..

スクリーンショット 2020-09-09 20.12.13.png

Je pense que des problèmes similaires peuvent survenir côté serveur, mais comme il s'agit d'une application Android dans mon projet, si 5 PR sont émis à la fois comme décrit ci-dessus, consultez la branche 5 fois, construisez-la et installez-la. J'ai dû vérifier l'opération.

Bien sûr, vous pouvez réduire le risque en construisant un test sur CI et en l'automatisant, mais dans le cas d'une application, vous êtes toujours inquiet de l'effondrement de l'interface utilisateur, alors j'aimerais vérifier visuellement l'opération une fois avant la fusion. .. Ou si vous déployez chaque branche avec CI, vous n'avez pas à vérifier, mais c'est toujours un peu difficile de simplement télécharger, installer et démarrer 5 fois.

Solution

En tant qu'approche à laquelle j'ai pensé, j'ai envisagé une méthode de ** fusion des branches de chaque PR émis par Dependabot en une seule branche, en construisant à partir de CI, et en laissant un lien vers le binaire comme commentaire pour chaque PR **. .. Je vais vous présenter la méthode actuelle ci-dessous.

Consolider les branches publiées par Dependabot

Tout d'abord, construisons un script Ruby qui extrait et intègre toutes les branches Git mises à jour.

  1. ** Obtenez une liste de branches commençant par la chaîne dependabot / **
  2. ** Créez un fichier de correctif pour la validation de mise à jour à partir de la branche actuelle (develop) **
  3. ** Appliquer tous les fichiers de correctifs obtenus en 2. **

(Le pouvoir rubis est en bas, alors ne le manquez pas: priez :)

Gemfile


source 'https://rubygems.org'

gem 'git'

merge_dependabot_branchs.rb


require "git"

dependabotBranchs = []
git_client = Git.open(Dir.pwd)
git_client.fetch
git_client.branches.each do |branch|
  if branch.name.start_with?('dependabot/')
    dependabotBranchs.append(branch)
  end
end

return if dependabotBranchs.size <= 0

dependabotBranchs.each do |dependabotBranch|
  system("git format-patch -1 #{dependabotBranch.gcommit} --unified=0")
end
system("git apply 0001-*.patch --unidiff-zero")

Un petit commentaire

La clé ici est de savoir comment créer et appliquer un fichier de correctif. Je génère un fichier de patch avec git format-patch, mais ** généralement les changements dans les 3 lignes avant et après sont également inclus dans Diff **. En d'autres termes, s'il y a déjà des changements dans les 3 lignes avant et après, comme lorsque les lignes de la bibliothèque mise à jour sont côte à côte, cela sera jugé comme un conflit et ne sera pas automatiquement intégré. Les conflits ne devraient pas se produire si seules les modifications de la mise à niveau de version par Dependabot sont extraites, donc même s'il y a des modifications dans la ligne proche, nous aimerions que vous les intégriez sans les considérer comme des conflits. Par conséquent, en ajoutant le paramètre --unified = 0, les lignes précédente et suivante sont ajustées pour ne pas être incluses dans le Diff. ([Référence](https://git-scm.com/docs/git-format-patch#Documentation/git-format-patch.txt --- unifiedltngt))

De plus, j'ai utilisé une astuce lors de l'application d'un fichier de correctif avec git apply, et ** normalement l'appliquer avec un fichier de correctif qui n'inclut pas les lignes précédentes et suivantes (avec --unified = 0 spécifié). Echouera **, donc le paramètre --unidiff-zero est ajouté afin que les lignes précédentes et suivantes soient ignorées et appliquées. (Référence)

Le traitement dans ce domaine était insuffisant avec ruby-git que j'utilisais au début, donc je me fie à l'appel système.

À ce stade, vous devriez avoir une branche avec des mises à jour de bibliothèque intégrées, donc si vous construisez dans cet état, vous pouvez générer un binaire intégré.

Mettre un lien vers le binaire intégré dans le PR émis par Dependabot

En supposant qu'un binaire est généré sur CI et qu'une URL de lien est également émise, construisons un script Ruby qui laisse un commentaire sur le PR généré par le Dependabot original. Cette fois, utilisez l 'API Github au lieu de Git pour extraire.

  1. ** Extraire les branches commençant par la chaîne dependabot / dans la requête d'extraction actuellement ouverte **
  2. ** Collez la liste de pull request intégrée avec le lien vers le binaire en tant que commentaire pour la pull request extraite **

(Code inférieur de puissance Ruby)

comment_dependabot_prs.ruby


require 'net/http'
require 'uri'
require 'json'

uri = URI.parse("https://api.github.com/repos/ignis-ltd/with_android/pulls")
request = Net::HTTP::Get.new(uri)
request["Accept"] = "application/vnd.github.v3+json"
request["Authorization"] = "token #{ENV['GITHUB_API_TOKEN']}"

req_options = {
  use_ssl: uri.scheme == "https",
}

response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
  http.request(request)
end

dependabotPullRequests = []
responseBodyJson = JSON.parse(response.body, symbolize_names: true)
responseBodyJson.each do |pull|
  if pull[:head][:ref].start_with?('dependabot/')
    dependabotPullRequests.append(pull)
  end
end

dependabotPullRequests.each do |pull|
  uri = URI.parse("https://api.github.com/repos/ignis-ltd/with_android/issues/#{pull[:number]}/comments")
  request = Net::HTTP::Post.new(uri)
  request.body = JSON.dump({
    "body" => "Deployed a binary that merged the following branches\n" + dependabotPullRequests.map { |pull| pull[:html_url] }.join(" ") + "\n\n#{ENV['INSTALL_PAGE_URL']}"
  })
  request["Accept"] = "application/vnd.github.v3+json"
  request["Authorization"] = "token #{ENV['GITHUB_API_TOKEN']}"

  req_options = {
    use_ssl: uri.scheme == "https",
  }

  response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
    http.request(request)
  end
end

Un petit commentaire

Fondamentalement, il est simplement implémenté directement à l'aide de l 'API Github.

Les variables d'environnement suivantes sont requises pour exécuter le script Ruby ci-dessus. Veuillez modifier l'URL de l'API Github le cas échéant N'incluez pas directement les informations de jeton Github, mais utilisez des variables d'environnement secrètes CI.

Si vous pouvez atteindre ce stade, vous pouvez laisser les commentaires suivants dans la demande d'extraction émise par Dependabot. Vous pourrez vous référer à la branche que vous avez intégrée et au binaire intégré à partir de toute Pull Request.

スクリーンショット 2020-09-09 20.12.44.png

Exécutez des scripts et des builds à temps dans CI après avoir exécuté Dependabot

Dans mon projet, Dependabot s'exécute à 9h00 le lundi, j'ai donc ajusté le CI contenant ce script pour qu'il s'exécute à 10h00 le lundi. Voici un exemple de configuration dans Bitrise. Veuillez ajuster cette zone à votre convenance.

スクリーンショット 2020-09-10 2.43.58.png

Supplément ou mise en garde

Le script qui intègre la première branche Dependabot est basé sur les informations de la branche de Git, mais le script pour les commentaires ultérieurs recherche basé sur PullRequest, donc (je ne pense pas que ce soit fondamentalement) PullRequest S'il existe une branche Dependabot non publiée, ou si vous apportez des modifications à la branche ou au PR pendant la construction, le contenu du commentaire et l'état réel du binaire seront différents.

De plus, comme le nom de la branche est recherché pour une correspondance de préfixe avec la chaîne de caractères dependabot /, il ne fonctionnera pas correctement lorsque la branche correspondante est créée manuellement, il peut donc être nécessaire de fournir une logique de jugement légèrement plus stricte. (Une sensation d'usure horizontale)

fin

C'était tout à fait une compétence, mais je pense que cela entraînera une réduction des coûts car vous pouvez vérifier l'opération une fois par semaine. Je ne pense pas qu'il y ait autant de bibliothèques dans un petit programme, donc vous ne ressentirez peut-être pas le besoin de le faire, mais si l'échelle dépasse 100 000 lignes, le nombre de bibliothèques installées sera énorme et le coût du contrôle de fonctionnement sera élevé. Ne peut pas être ridicule, donc en prenant de telles mesures, il est devenu possible de tirer le meilleur parti de Dependabot.

Aussi, cette fois, nous parlons d'intégrer Dependabot sur CI, mais comme on peut difficilement parler du côté CI uniquement de la partie de l'écriture d'un script et de son intégration avec l'esprit, je voudrais parler quelque part. ..

Recommended Posts

Une histoire qui a rendu aussi facile que possible la vérification de l'opération lors de la création automatique d'une mise à jour de bibliothèque PR avec Github Dependabot
J'ai créé une action GitHub qui facilite la compréhension du résultat de l'exécution de RSpec
L'histoire de rendre possible la construction d'un projet qui a été construit par Maven avec Ant
Une histoire qui a rendu pratique avec Kotlin qu'il est difficile d'exécuter une animation en continu sur Android
Création d'une bibliothèque qui facilite la gestion des préférences partagées Android
Lors de l'importation de CSV avec Rails, il était vraiment facile d'utiliser la commande nkf
Une histoire sur l'exécution de Sprint-boot avec kubernetes (GKE) et l'échec de la connexion à CloudSQL
Une histoire à laquelle j'étais accro lors de l'obtention d'une clé qui a été automatiquement essayée sur MyBatis
Java: Une histoire qui m'a mis mal à l'aise quand on m'a appris à comparer des chaînes avec des égaux sans raison.