Kekeの日記

エンジニア、読書なんでも

Siderでコードレビューを自動化してプロジェクトのエントロピーを維持する

f:id:bobchan1915:20181219233948p:plain

本記事

本記事では自動コードレビューができるSider(旧SideCI)を使ってみようかなとおもいます。

よくミスタイプとかして、それを修正するプルリクが必要になったりするとプロジェクトのエントロピーが圧倒的に増大するので防ぐためにも導入してみます。

インストール手順

STEP1: 許可するリポジトリを選択する

まず、最初に以下のような画面になります。

f:id:bobchan1915:20181219222945p:plain

ここで「Add a new organization」を選んでリポジトリを追加しましょう。

そして許可するとSTEP1が始まります。

f:id:bobchan1915:20181219224233p:plain

所感セクションでもありますが、ここには権限を与えたリポジトリのみで、検索しても権限を与えていないリポジトリが出るわけではありません。

STEP2: 設定を行う

まず、言語を選択します。

f:id:bobchan1915:20181219224817p:plain

今回はPythonを使ってやっていきます。

詳細設定(Tools)の中では、Pythonを選択すると勝手に選択してくれます。

f:id:bobchan1915:20181219224909p:plain

ここは任せたほうがいいでしょう。

また、テストモードというものを選択できるみたいです。

例えばプルリクエストでコードレビューの結果がStatusとして通らず、Mergeできない、といったような状況を防げます。

f:id:bobchan1915:20181219225145p:plain

プルリクエストがないと以下のようになるそうです。

f:id:bobchan1915:20181219225218p:plain

STEP3: プルリクを出してみる

プルリクを出してみると解析が実行されます。

f:id:bobchan1915:20181219225527p:plain

Siderの中ではIssueとは、コードレビューの結果、問題が潜んでいそうな箇所を指すようです。

f:id:bobchan1915:20181219225623p:plain

幸いに、何も問題はないようです。

f:id:bobchan1915:20181219225704p:plain

プルリクをステータスも以下のようになっていました。

f:id:bobchan1915:20181219225747p:plain

間違ったコードをPushして動作確認

Missspellだけにひっかかてほしいので以下のように間違いを全置換します。

f:id:bobchan1915:20181219231024p:plain

  • default -> defalutとしました。

すると以下のような結果になりました。

f:id:bobchan1915:20181219231122p:plain

問題を修正するためのアクションをとる

問題があると

  • Githubでコメントする
  • その場で「クローズ」する

でアクションを取る必要があります。テストモードではないので、このままだとMergeできません。

f:id:bobchan1915:20181219231348p:plain

Mergeできないのは、とても幸いですが。間違ったコードを統合しなくて済みます。

1. Githubでコメントをする

ボタンを押すと、以下のように確認をする必要があります。

f:id:bobchan1915:20181219231549p:plain

では、権限を渡してみましょう。

f:id:bobchan1915:20181219231530p:plain

するとコメントができるようになって、コメントしてクローズしたりできます。以下のようにコメントができています。

f:id:bobchan1915:20181219231809p:plain

2. 普通にクローズする

特に何もなくクローズできます。

f:id:bobchan1915:20181219231914p:plain

修正してPushする

再度、修正してPushします。

f:id:bobchan1915:20181219232143p:plain

無事、誤字がなくなっています。

所感

よい点

1. 権限を追加して、設定をぽちぽち

いままでの流れでhoeghoge.ymlみたいなのを一切書かずにGUIで勝手にやれました。

非常に導入しやすいです。

2. UIがかわいい

私は炭酸飲料が大好きなので、本プロダクトは可愛いと思ってしまいます。

3. コードにタイポなどの間違いが未然に防げる

ある程度ですが、未然にミスタイプなどのタイポを防ぐことができます。

これはチームが少なくても、チームが大きくても大きな恩恵となるでしょう。

残念な点

1. 最初に特定のリポジトリのみにアクセスをするとGithubから権限を編集する必要がある

例えば以下の状態のときに新しいリポジトリを追加したりしたいとしましょう。

f:id:bobchan1915:20181219223444p:plain

権限を与えてないので追加はできないのですが、まるでリポジトリが存在していなかのように検索ができません

f:id:bobchan1915:20181219223538p:plain
`

そのようなときはGithubにアクセスして設定ページから権限を渡さないといけません。

f:id:bobchan1915:20181219224053p:plain

2. Historyみたいな機能がない

CircleCIのHistoryのように過去の結果がみたいなと思ったときに見られないので辛いです。

こちらはCircleCIのダッシュボードです。

f:id:bobchan1915:20181219232359p:plain

3. 結果のUIがわかりにくい

最初に目に付くトップに完了とあるし、赤くもないので「成功したの!」って思ってしまいます。

f:id:bobchan1915:20181219232637p:plain

最も見やすいところに「結果」を表示してほしいです。

4. コードレビューのツールがわからない

ツールがいくつもありました。

しかし、どれを使うべきかは勝手に選んでくれるとはいえ知識が必要で、結果をみるのにも知識が必要です。

  • 「すべてOK」だったとしても何か、どのくらいの基準でOKだったのか
  • 必要としているコードレビューはできているのか

などテストと違って「そのパッケージが何をしているのか」を知らなければなりません。

テストだったら、テストの範囲は自分で決められていました。

5. 宣言的に書けない

長所の1.と相反しますが、ymlなどで書きたいです。

なぜなら、チーム内では複数の同じ言語のリポジトリなら同じ設定にしたいし、同じルールでレビューしたいからです。

また、チームの外やあらゆる組織で共有したい上に、アップデートの差分などもGithubで管理した方が便利、安全だからです。

まとめ

デメリットの方が項目は多いですが、量ではメリットの方が多いように感じます。

コードレビューは他にもツールがあり、比較となれば以下の記事をご覧ください。

開発効率を上げる!コードレビュー自動化サービス4選を使ってみた【SideCI・Codacy・Hound・Scrutinizer】 - paiza開発日誌

個人的にはプルリクまでを出してくれたり、他にも機能があるRocroの方がまだ使いやすいように感じます。

Rocro

どちらも日本製なので、頑張って欲しいです。

今日はここらへんで終わります。ありがとうございます。

Raspberry PiとImgurを使って無料で自宅監視カメラLINEボットを構築する

f:id:bobchan1915:20181217053625p:plain

本記事

本記事では、実際にRaspberry Piのカメラモジュールを使って監視カメラサービスを構築してみようと思います。

完成形は以下のようにかまると呼びかけると家の画像を返信してくれるものです。すべて無料の制限内にします。

f:id:bobchan1915:20181217073525j:plain

モチベーションとしてはカメラモジュールの使い方などを解説している記事は多くありますが、実際にCloud Nativeな技術と組み合わせて解説し、運用しているものがないと思ったからです。

使うもの、環境は以下のようです。

Raspberry Pi Model B

Raspberry Pi 3 Model B V1.2 (日本製) 国内正規代理店品

Raspberry Pi 3 Model B V1.2 (日本製) 国内正規代理店品

Raspberry Pi Camera Module V2

Imgur

imgur.com

Cloud Functions

Google Cloud Functions に関するドキュメント  |  Cloud Functions  |  Google Cloud

LINE message API

LINE Developers

Raspberry Piのセットアップ

1. Wifi設定

まず、RaspberryPiのWifiを設定します。

CUIから設定しなければならないので以下のように設定します。

sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

そして以下のようにnetworkを書き込みます。

network={
    ssid="YOUR__SSID"
     psk="PASSWORD"
}

また、これを設定してから再起動する必要があります。

sudo reboot

2. 環境構築

以下のように環境構築をします。

sudo apt-get update && sudo apt-get upgrade

そしてPython3.6.0をインストールします

sudo apt-get install build-essential tk-dev libncurses5-dev libncursesw5-dev libreadline6-dev libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev

wget https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tar.xz
tar xf Python-3.6.0.tar.xz
cd Python-3.6.0
./configure
make
sudo make altinstall

3. 静的IPを設定する

毎回毎回、IPアドレスが変更されると大変なので静的IPを取得します。

以下のように設定します。

sudo vim /etc/dhcpcd.conf

そして、以下のように書き換えます。

interface wlan0
static ip_address=hogehoge
static routers=hogeheoge
static domain_name_servers=hogehoge

そして、また再起動します。

sudo reboot

4. カメラモジュールをつける

RaspberryPiの開発者の一人の解説動画があるので、安心してつけられました。

www.youtube.com

Imgur

f:id:bobchan1915:20181216231523p:plain

Imgurとは画像シェアサービスです。主にgifなどを対象としています。

Hiddenという、いわゆるPrivateな画像を保存することができ、画像一つ一つにダイレクトリンクが渡されます。

そのリンクを知れれば、誰でも簡単にアクセスできるため気をつけてください。

最初に

迷ったらドキュメントを確認してください。バージョンアップが絶えずあるため、必要に応じて参照してください。

新しいバージョン(version 3)のAPIドキュメントは以下のリンクになっています。

apidocs.imgur.com

執筆時点(2018/12/16)で最新のバージョンとなっています。

アプリケーションを登録

まず、Imgur APIにアクセスするクライアントアプリケーションを登録する必要があります。

認証にはOAuth2.0を使っています。OAuth2.0の仕組みは、以前ブログにしました。

www.1915keke.com

簡単にいうと

  • 一旦、アプリケーションでログインして短命なコードをもらう
  • そのコードで認証を済ませて、アクセストークンをもらう
  • 今度からはそのトークンをもってサービスにアクセスする

という流れです。

まず最初にアプリケーションを登録してclient_idclient_secretをもらう必要があります。

ここでPostmanを使うと便利に登録できるので以下のサイトでダウンロードします。

www.getpostman.com

そしてドキュメントの右上のRun in Postmanを押してPostman for Macを選択します。

f:id:bobchan1915:20181216131638p:plain

すると以下のようにmacのPostmanアプリケーションでCollectionsとして開かれます。

f:id:bobchan1915:20181216152601p:plain

Collectionsとは、リクエストのまとまりです。

まず、Webページの方でSubmitして以下のようにコードをもらいます。

f:id:bobchan1915:20181216153055p:plain

ここでAuthorization callback URLにはhttps://www.getpostman.com/oauth2/callbackと入力ください。

あとはEmailを埋めるだけです。

そしてもらったclient_idclient_secretをもってして、Postmanの方で以下のようにOAuth2.0を設定します。

まずAccount-> Generate Access TokenからAuthorizationタブを選択します。

そして、Generate Access Tokenを押して以下のようにフォームを埋めます。

f:id:bobchan1915:20181216232242p:plain

そしてRequest Tokenを押すと以下のようになります。

f:id:bobchan1915:20181216153705p:plain

ここでログインするとAccessトークンを取得できます。

これでUse Tokenを選択してください。次から簡単にアクセストークンが使えるようになります。

f:id:bobchan1915:20181216233641p:plain

ここまででアクセストークンを取得することができました。

適当に画像をPostmanを使ってUploadしてみます。

以下のようにImage-> Image Uploadで使ってみます。ImageはファイルをドラッグするとBodyにつけることができます。

f:id:bobchan1915:20181216155600p:plain

無事、Hidden(Private)な画像をアップロードができました。

f:id:bobchan1915:20181216155952p:plain

このような感じで、次はスクリプトからImgurに画像をストアしていきます。

Pythonコードを書く

このセクションはアーキテクチャ図の以下の箇所に対応しています。

f:id:bobchan1915:20181217054056p:plain

1. 静止画を撮影するサーバーを立てる

f:id:bobchan1915:20181217054146p:plain

Postmanでは手でぽちぽちとHTTPリクエストを用意して、実行していました。これからはサービスにするためにもスクリプトで書いていきます。

まず、最初にサーバを立てましょう。RaspberryPiとの相性もいいのでPython製のFlaskという軽量Webサーバーを立てていきましょう。

import flask
from time import sleep
from picamera import PiCamera

app = flask.Flask(__name__)

@app.route('/kamaru')
def index():
    camera = PiCamera()
    camera.resolution = (1024, 768)
    camera.start_preview()
    sleep(2)
    camera.capture('test.jpg')
    return "Hello, World!"

if __name__ == '__main__':
    app.run(debug=True)

これによってtest.jpgの名前で保存されます。

2. Imgur APIへアップロードするスクリプト

f:id:bobchan1915:20181217054240p:plain

ここでは以下のようにHTTPリクエストでImgur APIへ送信します。

このスクリプトをupload.pyと名付けておきます。

import os
from os.path import join, dirname
import json
import requests
from dotenv import load_dotenv

dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)


url = 'https://api.imgur.com/3/image'
file = {'image': open('test.jpg', 'rb')}
headers = {'Authorization': 'Bearer ' + os.environ['IMGUR_ACCESS_TOKEN']}

res = requests.post(url, files=file, headers=headers)

data = res.json()

print(json.dumps(data, indent=4))

環境変数よりアクセストークンであるIMGUR__ACCESS_TOKENを取得します。

また、このアクセストークンやこれからの環境変数を設定するのは.envファイルに記述します。

IMGUR_ACCESS_TOKEN=

の右辺に渡してあげます。

ここまででアップロードできます。

python upload.py

また、これをFlaskサーバーにこの機能を載せます。upload.pyを先ほどのFlaskサーバーに載せると以下のようになります。

import flask
import os
from os.path import join, dirname
import json
import requests
from dotenv import load_dotenv
from time import sleep
from picamera import PiCamera

dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)

app = flask.Flask(__name__)

def capture_image():
    camera = PiCamera()
    camera.resolution = (1024, 768)
    camera.start_preview()
    sleep(2)
    camera.capture('test.jpg')

def upload_image():
    url = 'https://api.imgur.com/3/image'
    file = {'image': open('test.jpg', 'rb')}
    headers = {'Authorization': 'Bearer ' + os.environ['IMGUR_ACCESS_TOKEN']}

    res = requests.post(url, files=file, headers=headers)
    data = res.json()

    return json.dumps(data, indent=4)

@app.route('/')
def index():
    return "Hello, World!"

@app.route('/kamaru')
def index():
    capture_image()
    data = upload_image()

    return data

if __name__ == '__main__':
    app.run(debug=True)

これによって/kamaruでリクエストがあると、画像をアップロードできるようになりました。

ここで/を残したのはヘルスチェックというもので、死活管理に使うためです。

また、ローカルホストを公開するためにはngrokを使います。

ngrok.com

以下のようにインストールすることができます。

wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip
unzip ngrok-stable-linux-arm.zip
sudo mv ngrok /usr/local/bin/

Flaskサーバーをバックエンドで立てます。

python3.6 server.py &

そしてngrokで公開します。

ngrok http 5000

とするとhttpsのURLスキーマのサーバーを立てることができ、どこからでもアクセスをすることができます。

試しに

curl https://77dr8983a.ngrok.io

=> Hello, World!

と返ってきました。

これだとssh接続が切れるとngrokも使えなくなるため以下のようなコマンドを打ちます。

nohup ngrok http 5000 &
curl localhost:4040/status | grep ngrok.io

そのURLを使ってください。

3. LineBotのサーバーを書く

f:id:bobchan1915:20181217053807p:plain

今回はコストと手軽さを考えてCloud Functionsで構築してみようと思います。

最初に環境変数を使うための.env.ymlファイルを作成します。先ほどのPythonスクリプトとは別ディレクトリか、別リポジトリにしておくと管理がしやすいでしょう。

LINE_CHANNEL_ACCESS_TOKEN:
LINE_GROUP_ID:
MY_RASPBERRY_PI_URL:

そして以下のようにハンドラーを作ります。

const line = require('@line/bot-sdk');
const client = new line.Client(config);

function handleEvent(event) {
    if (event.type === 'message' && event.message.type === 'text') {
        if (event.message.text.match(/^かまる/g)) {
               // 内容を実装する
        }
    }

    return Promise.resolve("error")
  }
  
exports.handler = function lineBot (req, res) {
    Promise
      .all(req.body.events.map(handleEvent))
      .then(result => res.status(200).send(`Success: ${result}`))
      .catch(err => {
          console.log(err.toString())
          res.status(400).send(err.toString())
        });
};

これはかまると呼びかけると反応するのものです。しかし内容はまだ実装されていません。

これからは// 内容を実装するという項目で実装していきます。

大枠、このような実装になるでしょう。

const raspberryPiURL = process.env.MY_RASPBERRY_PI_URL
const options = {
    url: raspberryPiURL,
    method: 'POST'
}
request(options, (err, res, body)=>{
    var message
    if (err) {
        console.log(err)
        message = getErrorMessage(err.toString(), currentDate)
        return client.replyMessage(event.replyToken, message);
    }

    var message = {
        imageURL: body.data.url
    }

    message = getResponse(message)
    return client.replyMessage(event.replyToken, message);
})

このようにCloud Functions`からRaspberry Piへトリガーをしています。

一応、リクエストが失敗してもLINEアプリ側にはエラーメッセージを返しています。 これによってのちにFlaskサーバーが起動していなくても、何かしらのメッセージは返せるようになっています。

getXXXのメソッドはLINE Message APIのFlex Messageのメッセージを構築しています。ここらへんはフロントエンドの話なので好みに合わせて作成しています。

本記事では、本質的なところではないので、省略します。

Flex Messageを使ったことがなければ

response = {type: 'text', text: "レスポンス"}

などのように簡単なテキストメッセージでも構いませんのでトライしてみてください。

ここまででLineBotのレスポンスはできました。

以下のようにデプロイをします。

gcloud beta functions deploy KamaruLineBot --env-vars-file .env.yml --trigger-http --region=asia-northeast1 --entry-point handler

取得したCloud FunctionsのURLはLINE DEVELOPERSのご自身のBotのWebhookURLに設定します。

f:id:bobchan1915:20181217063134p:plain

試しに何かメッセージを送ってみるといいでしょう。

動作確認

試しにかまると打つと以下のようにでます。

f:id:bobchan1915:20181217073525j:plain

勝手にNature Remoから取得した温度を追加してしまいました。

しかしながら、家の監視ができていると思います。

定期的にPushする

f:id:bobchan1915:20181217074027p:plain

定期的にPushするにはreplyMessagepushMessageにして、グループIDを入力します。

そしてCloud Schedulerで自分が見たいスパンでリクエストを送るようにしてください。

まとめ

ハードウェア代金がかかるものの、ソフトウェアによって自分好みの機能、デザインがすることができるので、やはり既製品を買うのとはわけが違うなと思いました。

これからも家電コントローラのNature Remoや、電子回路を組んで、楽しくやっていこうと思います。

最後まで、ありがとうございました!

Argo CDによってGKEでGitOpsをする

f:id:bobchan1915:20181206151759p:plain

はじめに

最近読んだ鎌倉時代の歌人、鴨長明の『方丈記』にも

ゆく河の流れは絶えずして、しかももとの水にあらず。

とありますが、私たちのソフトウェアも然りです。

毎日更新され、デプロイされ、時に不可逆に破壊され。そして、ちぐはぐな形に落ち着くまで絶えず変更されているように感じます。

そのようなことにならないためにもOpsやCDについて注目しています。

本記事

本記事はKubernetes Advent Calendarの15日目の記事になっています。

どうも、私はもうすぐ大学を卒業(予定)の@timtimtim1026です。

コメントや間違いなどがあればDMやリプを軽く飛ばしてもらえると嬉しいです。

今回は、GitOpsを実現するためのツールとしてArgo CDが注目を集めているので、GitOpsについて考えていこうと思っています。

また今回はたまたまArgo CDを使ってみますが、だいたいのGitOpsに導入しやすいソフトウェアは、似たような構成なので、体系的に学べる記事にできれば嬉しいと思っています。他にもWeave FluxやJenkins Xなどがありますが、それにも通用する知識をつけられれば何よりです。

コンテンツ

本記事の目次は以下の通りです。

興味がある項目だけでも、ぜひ読んでみてください。

  • はじめに
  • 本記事
  • コンテンツ
  • GitOpsについて
    • GitOpsの定義
    • Cloud Nativeと継続的デリバリー
    • 構成
  • Argoについて
    • まずArgoとは
    • Argo CDとは
    • 構成アーキテクチャ
      • 1. GitOpsを実現する
      • 2. CIと接続
      • 3. CDとしてDeployする
  • 準備
    • GCPにCLIからログインする
    • Projectを作成して設定
    • テストクラスタを作成
    • Credentialを取得
  • Argo CDをインストール、基本を理解する
    • ClusterRoleBindingを作る
    • Namespace作成してArgo CDを入れる
    • Argo CD CLIをインストールします
    • Argo CD API サーバーにアクセス
    • WebUIからGithub Hookの設定をする
    • アプリケーションを設定する
    • 同期する
    • 実際にGitOpsになっているかを確認
  • 機能のDeep dive
    • リアルタイム同期のためのWebhook
    • Application(リポジトリ)をまとめるためのProject機能
    • APIドキュメントをみる
    • Hookを設定する
    • argocdコマンド一覧
  • まとめ
  • 参考文献
    • Argo公式ドキュメント
    • Japan Container Daysであった発表
    • KubeCon North America 2018での発表 
続きを読む

CircleCIでSpinnakerのデプロイメントパイプラインをデプロイする

f:id:bobchan1915:20181206211505p:plain

どうも、もうすぐ大学卒業の@timtimtim1026です。

もしコメントなどがございましたらDMなり、リプライしてください。

本記事はCircleCI Advent Calendarの14日目の記事になっています。

本記事

この記事では、CircleCIを使ってSpinnakerのデプロイメントパイプラインをデプロイする方法を解説します。

twitter.com

今回の目的としては、CircleCIを使いながら継続的デリバリー(以下、CD)を構築する一例を知ってもらうことです。また、そのなかで紹介するCircleCIの知識が何かしら役に立てられればいいなと思います

考え方としては、今回使うCircleCIやSpinnakerに限らずあらゆるCDにも、CIにも言えることなのでSpinnakerだけに限った話ではありません。

続きを読む

Terraformで運用しているLineBot家計簿をGCPで使う

f:id:bobchan1915:20181210104636p:plain

はじめに

私は自分で作ったLineBotを使って家計簿をつけています。

クラウドにはGoogle Cloud Platform(GCP)の無料枠を使っていて、データベースにはCloud SQL(MySQL)を使っています。

自分でデータを集めているので、以前にもApache Supersetなどを用いてデータ分析をしていました。

www.1915keke.com

しかしながら、無料枠を使っていく中で、無料枠が終わって、別の保有しているアカウントに移行したい時に**まったく構成管理が宣言的にできていないことに気づいて、今回はTerraformを使ってやっていこうと思います。

続きを読む

Lighthouseを使ってVue.jsで書いたポートフォリオサイトを爆速化する

本記事はVue.js Advent Calendar 2018の第3日目の記事です。

はじめに

GCPの無料枠が終了したため、ポートフォリオサイトはクローズドしましたが、引き続き本記事はご覧いただけます。

私は以前、インターンシップなどの選考に備えて自分のポートフォリオサイトなるものを作りました。

しかし、あまりに速くデプロイしたかったことからパフォーマンスについて一切無視した結果、とてつもなく遅いサイトができてしまいました。

結局、他人に見せるには恥ずかしいサイトになってしまい、今回の記事で爆速にできればと思います。

サイトはこちらで、SEO対策やOGPも実装したので以下のように展開されます。

f:id:bobchan1915:20181216014218p:plain

要約

  • 自分のポートフォリオサイトがロードまで8秒近くかかっていたのがキャッシュ込みで0.3秒くらいになった
  • クリティカルレンダリングパスを知ることが重要
  • Chrome DevToolを使い倒すとよくわかる。Lighthouseは便利だし、解決策をくれる
  • いろんなサイトをみても最初は画像の質が悪い(サイズが大きいなど)の問題が目立つので注意する
続きを読む

Github Actionsが使えるようになったので使ってみる

本記事

本記事では、少し前にアナウンスされたGithub ActionsというCIツールのようなものを実際に触ってみて、現時点での使い勝手をまとめてみようと思います。

実際に使えるようになると以下のようなタブが追加されています。

f:id:bobchan1915:20181202180405p:plain

初めて使ってみる

ActionページにいってWorkflowを作ってみる

Actionページに行くと以下のようなページでWorkflowを作り始められます。

f:id:bobchan1915:20181202180539p:plain

"Create a new workflow"を押して開始してみます。

続きを読む

Perlで現ブランチのPull RequestのURLを取得するtig用のスクリプトを書いた

f:id:bobchan1915:20181202105045p:plain

本記事

私は、これまでtigでpushをするとブラウザでプルリク作成に飛ぶスクリプトを使っていましたが、現在のブランチのプルリクをみたい時があります。

今回はperlを使って、その機能を実装するスクリプトを書ければなと思います。

また、スクリプトはすべてfishスクリプトなので注意をしてください。

続きを読む