🎉

【初OSS】クリスマスをカラフルに彩る🎉!ターミナルで動作するGo製マテリアルカラーピッカー!🎄

2021/11/22に公開

この記事は🎄Qiita Advent Calendar 2021 - Go🎄に参加します。他のサービスからの投稿お作法がわからないので、1週間ぐらい早いけどいっときます!
Goのカレンダーは大人気!ぜひ他の方の記事もご参照下さい!

この記事の趣旨は「Goならこんな感じのツールを簡単に作れるよ」というGoの布教です 🎄


はじめに:制作物について

Demo

mcpick - demo

Overview

https://github.com/tenkoh/mcpick

  • ターミナル上で動作する軽量なアプリケーション(TUI: Terminal User Interface)
  • Web系開発で必要になりがちな色コードを、キーボードのホームポジションから指を離すことなく取得
  • Goの特長であるクロスプラットフォーム対応を活用し各種OS, Archに対応
  • 見た目をカラフルにしてクリスマスらしさを演出🤣🎉🎉
  • 使い方はREADMEを見てね

本記事の内容

  • 技術面について
    • 利用した技術
    • クラスプラットフォーム対応からの学び
    • Tips: locale問題
  • OSSとしてのリリースについて
    • リリース特有の準備
    • クロスプラットフォームビルド
  • 今後やりたいこと
  • おわりに

技術面について

利用した技術

  • Goには簡単にTUIを作れる素晴らしいパッケージがいくつも存在します。今回はその中でtviewを使わせて頂きました。
    • 公式リポジトリにて複数のDemoが掲載されており、またドキュメントも充実しているため、比較的手軽に導入することができます。
    • とは言っても細かい部分については不明点もありました。その際にはminefutoさんの下記の記事も大変参考になりました。ありがとうございます。

https://zenn.dev/minefuto/articles/cafc02dd63f65d

  • キーボードショートカットも柔軟に設定できます。ホームポジションから手を動かさないコンセプトに従ってカーソル移動をvimキーバインドで行うなど、やりたいことがサッと実装できる良い開発体験を得ることができました。
  • 後述するlocale依存問題で躓いてしまいましたが、それでも3日ぐらいで作りたいものが作れました。内部的にも難しいことは一切しておりません。

クロスプラットフォーム対応からの学び

自分のアプリケーションの機能を必要最小限にし、他のアプリ/コマンドと組み合わせやすくすることが大切だな、と学んだ気がします。

  • 当初は選択した色の色コードをクリップボードにコピーする、という機能を実装したかったのですが、クリップボードが絡むと各種OSへの対応が分かれてしまいます
  • Goのパッケージでクリップボード操作を行うものも見受けられましたが、結局Linuxではユーザに指定のツールを事前インストールさせておく必要があるなど、縛りが増えてしまうように感じました
  • そこでLinuxツールの哲学(?)に則り、小さく機能を分割することを考えました。自分のアプリケーションからは標準出力への出力だけを行うようにすれば、あとはパイプでユーザが好き勝手にやってくれたら良いのです。得意なことは得意な人にやってもらう、という考えの有用さを改めて感じることができました。

Tips: locale問題

  • とても面白いのですが、en_US.UTF-8環境以外では文字が欠けたり、レイアウトが崩れたりと、画面表示上でのトラブルが起きました(当方では日本語環境のWindows10, MacOS Big Surで確認)
    • 調べてみると既知の案件ですlink
  • tviewのHello,Worldしてみたらコレ🤣

ello, world

  • いろいろ試しましたが、最終的にはこちらを使わせてもらい、文字幅をコントロールして対応しました。(神パッケージ)
main.go
func init() {
	runewidth.DefaultCondition = &runewidth.Condition{EastAsianWidth: false}
}
  • こうしたパッケージを使わない対応としては、Issueでも報告されているように、アプリケーションの実行中だけ言語設定を上書きする方法があります。
    • Unix系であればLC_CTYPE="en_US.UTF-8"
    • Windowsであればchcp 437
    • Goのプログラム中でtviewのアプリケーションが走る前にos.Setenvなどしてあげれば良いかと思いきや、それでは解決しませんでした。(理由はまだ紐解けていません)
    • ということでこちらの方法を採る場合、別途シェルスクリプトなどを使って言語設定変更→プログラム実行→言語設定復元というフローにする必要があり、やや煩雑だなと思います。

OSSとしてのリリースについて

せっかく作ったものなので、バイナリを配布していろいろな人に使ってもらいたいものです。そこで初めてのリリースを行いました。

リリース特有の準備

  • Version情報の埋め込み、表示オプションの設定
  • わかりやすいREADME

クロスプラットフォームビルド

  • Goの持ち味の1つとして容易にクロスプラットフォームビルドができることが挙げられます。大変分かりやすい記事がネットにはたくさんありますので、本記事では詳細には触れません。
  • とはいえ様々なOS、Archに対応すべくちまちま作業するのは大変です。goreleaserのような素晴らしいツールもありますが、初めての経験ですので、まずは自分でトライしてみることにしました。
    • どのようなプラットフォームを対象にするかは、偉大なる先達のリリースページを参考にしました。
    • GitHubへのリリースは手動に甘んじることにします。
    • Makefileを使いこなすと玄人感がありますが、全く未知なのでまずは諦めました。代わりにシェルスクリプトを書いて対応します。
    • リリースに当たってはタグ付けを事前に済ませておくものとし、タグを-ldflagsにより埋め込むようにしました。
    • 最終的に以下のようなスクリプトで、比較的簡単にビルドを行うことができました。やったね。
シェルスクリプト例
#!/bin/bash
VERSION=$(git describe --tags)
echo $VERSION
THIS_GOOS=$(go env GOOS)
THIS_GOARCH=$(go env GOARCH)
RELEASE_DIR="release"
ARTIFACT_DIR="artifact"
rm -rf $ARTIFACT_DIR

RELEASE_TARGET=("linux/arm" "linux/arm64" "linux/amd64" "windows/amd64" "darwin/amd64" "darwin/arm64")
for t in ${RELEASE_TARGET[@]}
do
    DIST=$RELEASE_DIR"/"$t"/mcpick_"$VERSION
    tmp=(${t//// })
    GOOS=${tmp[0]}
    GOARCH=${tmp[1]}
    SUFFIX=""
    if [ $GOOS = "windows" ]
then
            SUFFIX=".exe"
    fi
    DIST_BIN=$DIST"/mcpick"$SUFFIX
    go build -o $DIST_BIN -ldflags "-X main.version=$VERSION"
    cp LICENSE README.md $DIST
    mkdir -p $ARTIFACT_DIR 2>/dev/null
    if [ $GOOS = "linux" ]
        then
            tar cfvz $ARTIFACT_DIR"/mcpick_"$GOOS"_"$GOARCH".tar.gz" $DIST
    else
        zip -r $ARTIFACT_DIR"/mcpick_"$GOOS"_"$GOARCH".zip" $DIST
    fi
done
GOOS=THIS_GOOS
GOARCH=THIS_GOARCH
rm -rf $RELEASE_DIR

今後やりたいこと

  • Homebrewでもインストール可能にしたい
  • 既知のIssueは解消しておきたい(Windowsの日本語環境かつ特定のターミナルでは表示が崩れるなど)
  • OSS活動はおもしろい!自作ツールの制作&公開や、他のプロジェクトへのcontributeなど、幅を広げていきたい!(そのためには勉強や)

おわりに

  • Goの魅力の一端でも伝えられていたら幸いです
  • 年の瀬もLet's code with Go!
GitHubで編集を提案

Discussion