自分の全ての GitHub リポジトリを横断的に検索・文字列置換する
はてなブログを HTTPS 配信に切り替えたので、はてなブログへのリンク URL が http://
から https://
に変わった。
自分は GitHub リポジトリの README.md などにはてなブログへのリンクを貼っているので、全ての GitHub リポジトリを横断的に検索し、http://
でリンクを記述している箇所を特定して https://
に置換したいと考えた。今日はその記録をお届けする。
目次
前提
Ubuntu 18.04 で作業した。grep や sed などの各種コマンドを使うが、MacOS でやる場合は GNU sed を使うよう注意。
全ての GitHub リポジトリの情報を取得する
自分のアカウントで持っている GitHub リポジトリの数は、現時点で106個。
GitHub API を使えばリポジトリ情報が色々と拾えるが、ページャ制限があり、1回の API コールで100件分までの情報しか取得できない。そのため、106個あるリポジトリの全情報を取得するには、ページャを移動してデータを取得し、それらをマージする必要があった。
それを行うのが以下のコード。
# リポジトリの情報を100件ずつ取得する
$ curl -s 'https://api.github.com/users/Neos21/repos?per_page=100&page=1' > repos-1.json
$ curl -s 'https://api.github.com/users/Neos21/repos?per_page=100&page=2' > repos-2.json
# jq を使ってマージする
$ jq -s add repos-1.json repos-2.json > repos.json
# 一時ファイルを消す
$ rm repos-1.json repos-2.json
コレで、repos.json
に106個のリポジトリの情報が取得できた。GitHub リポジトリの情報は色々と有用なモノがあるので一部紹介しよう。
name
: リポジトリ名full_name
:オーナ名/リポジトリ名
html_url
: ブラウザで見られる URLclone_url
:git clone
時に指定できる URLhomepage
: リポジトリ説明欄の横に掲載できる URLsize
: リポジトリのサイズ (KB 単位)fork
: フォークしたリポジトリかどうか (Boolean)private
: プライベートリポジトリかどうか (Boolean)
GitHub リポジトリの「ホームページ URL」を確認する
今回、はてなブログの URL を書きそうな場所として homepage
部分もありそうなので、以下のように検索しておこう。
$ jq -r 'map(select(.homepage | startswith("http://neos21"))) | map(.clone_url)[]' repos.json
.homepage
が http://neos21
から始まる要素をフィルタし、その要素の .clone_url
を出力した。コレでいくつかヒットしたので、出力された URL にアクセスして「ホームページ URL」を書き換えた。
リポジトリ内のファイルを grep
し置換する
続いてリポジトリ内のファイルを編集していく。GitHub API だけで各リポジトリを参照、編集したりするのは大変だし、置換が失敗していたりするのに気付かなかったりするので、一旦全リポジトリをローカルに git clone
し、一括置換してやろうと思う。
一括 git clone
する
というワケで、まずは全リポジトリをローカルに git clone
するためのコマンドを作っていこう。
$ jq -r 'map("git clone --depth 1 " + .clone_url + " # " + (.fork|tostring))[]' repos.json
# 出力例
git clone --depth 1 https://github.com/Neos21/hoge.git # false
git clone --depth 1 https://github.com/Neos21/fuga.git # false
例えばこんな風にすると、git clone
コマンドの形で全リポジトリの情報を出力できる。フォークしたリポジトリについては扱いを別にしようかな、とか思ったので、コメントで .fork
の情報を出力してある。一旦コレをテキストエディタにコピペし、明らかに git clone
しなくても良さそうなリポジトリは除外している。
微調整が終わったら、そのファイルに Shebang #!/bin/bash
を書き足し、そのまま Bash スクリプトとして保存して実行してしまおう。どんどん git clone
されていく。
なお、コマンドに付与した --depth 1
とは、取得するコミットを直近のモノだけにするオプションで、コレによりリポジトリ全体をダウンロードせずに済むので、サクサクダウンロードできるだろう。
grep
結果に対し一括 sed
置換する
全リポジトリをローカルに落とせたら、次のようなコマンドを組み立てる。
$ grep --exclude-dir='.git' --exclude-dir='node_modules' --exclude='*.ttf' -inr 'http://neos21' ./
grep
の --exclude
で拡張子除外、--exclude-dir
でディレクトリ除外を指定している。この辺は各自のリポジトリの傾向を見てお好みで。
-i
は検索文字列の大文字・小文字を無視。-n
はファイル名と行番号を表示する。-r
で指定ディレクトリの配下を再帰的に検索する。
このようなコマンドで、まずは置換したい対象を把握しておこう。バイナリファイルなどがヒットしたり、Linux 環境でキレイに置換できなさそうな .bat
ファイルなどは目視で確認しておく。
置換対象が確認できたら、コマンドを次のように直して、実際に置換をする。
$ grep --exclude-dir='.git' --exclude-dir='node_modules' --exclude='*.ttf' -ilr 'http://neos21' ./ | xargs sed -i -e 's@http://neos21@https://neos21@g'
変更したのは、-inr
と書いていたオプション部分を -ilr
とした。-l
オプションは、ヒットしたファイル名のみを出力するモノだ。
対象のファイル名のみを取得し、xargs
と GNU sed
を使って、文字列置換を実施している。-i
(--in-place
) オプションで元ファイルに上書きをするが、-e
より手前に、分けて書くこと。-e -i 'コマンド'
のように書くとコマンドが失敗する。
$ grep -ilr 'HOGE' ./ | xargs sed -i -e 's/HOGE/FUGA/g'
↑ コレをイディオムとして暗記してしまおう。
自分のブログ URL にのみヒットするように、http://neos21
を検索文字列とし、コレを https://neos21
と置換しているワケだ。
置換が終わったら、再度 grep
のみを実施してみて、キレイに置換できていないところや、手作業で直した方が良い箇所を見つけて手直しする。
コミットとプッシュは面倒だが手作業でやると良いだろう。次のようなコマンドを作っても良い。
# 各リポジトリのディレクトリに移動したら、次のコマンドをコピペ実行する
$ git add . && git commit -m "Update URL" && git push && cd ../
コレで置換完了
というワケで置換完了。リポジトリ数が増えてきて大変なのでもうやりたくないが、自分の作業をこうやって自動化・効率化できるのは楽しい。