ゆるふわ技術日誌

エンジニア見習いの悪戦苦闘日記

【超小ネタ】Gitリポジトリ内にいるときにリポジトリのrootまで一発で戻りたい

Gitリポジトリの深い階層にいるときにリポジトリのルートまでさくっと戻りたいことってないでしょうか?

自分は cd でホームディレクトリまで戻ってからリポジトリのルートまで移動し直したり、 cd ../../../.. みたいなかんじで戻ったり(そして戻りすぎたり)してました。

gitコマンド使うとリポジトリのルートの絶対パスを取得できることを知ったのでリポジトリのルートまで戻るコマンドをエイリアス貼ってみました。

rootをとるコマンドは git rev-parse --show-toplevel 、これの結果をcdに渡してあげたコマンドをalias貼るだけでOKです。すばら。

rev-parseコマンド、結構いろいろできるみたいですね。

qiita.com

おしまい。

Golang インタフェース型のポインタを扱おうとしてハマった【たすけて】

注:いまいちすっきり解決してないので、誰か知っていたら教えてください。


年末クリーンアーキテクチャの本を読んだので、Golangで実践してみようと思ったら思わぬところでハマったメモ。(クリーンアーキテクチャするならオブジェクト指向の言語でするべきなのでは?というのはあるがGolangの勉強も一緒にしたかったというのが大きい)

簡略化するとこんなコードを書いていた。

package main

// 実際は引数受け取ったり値を返したりする
type SomeRepository interface {
    Create()
    Find()
}

// DBとかの技術的詳細に関わる値を持っているイメージ
type SomeRepositoryImpl struct {
    CollectionName string
}

func (repo *SomeRepositoryImpl) Create() {
    return
}

func (repo *SomeRepositoryImpl) Find() {
    return
}

func CreateSomeRepository() *SomeRepository {
    return &SomeRepositoryImpl{
        CollectionName: "Something",
    }
}

SomeRepositoryという抽象を実装したSomeRepositoryImplというのを作りたい、というあるあるなRepositoryのコード。Repositoryを使う側は技術的詳細であるDBに対する操作がどうなっているかということを知らなくて済む、というやつ。

ところが上記コードはコンパイルエラーになる。

エラーになるのは

func CreateSomeRepository() *SomeRepository {
    return &SomeRepositoryImpl{
        CollectionName: "Something",
    }
}

この関数のSomeRepositoryImplを作っているところで、Cannot use '&SomeRepositoryImpl{ CollectionName: "Something", }' (type *SomeRepositoryImpl) as type *SomeRepositoryというエラーになる。

いろいろ試行錯誤して、散々ハマったが、結局のところ答えはこう。

func CreateSomeRepository() SomeRepository {
    return &SomeRepositoryImpl{
        CollectionName: "Something",
    }
}

関数の返り値の型がポインタ型ではなくなった。どうやらインタフェース型のポインタというのはない?らしい。とりあえずうまくいってしまったので続きをやっていこうかなと思いつつ、これがなぜなのか?というのにすっきりとした答えが出せずにモヤモヤしている。

2020年反省会会場

紅白見ながら一気に振り返る。

https://yurufuwa-tech.hatenablog.com/entry/2020/01/06/091359

この記事の答え合わせ。


月2回以上の技術アウトプット

これは完全にできなかった。なんなら1月で崩壊してた気がする。

昇級する

これも叶わず。

そもそも自分のWillってなんなんだろうなあということに悩み続けた一年だった気がする。現在進行形で悩んでいるが、来年は新卒3年目、流石にそうも言っていられないのでバチっと決めていきたい。

アプリ以外の技術領域をやる

これはやった。一応Elmという言語を使ったWebフロントエンドの開発や12月にはGolangを使ったサーバーサイド開発もやったりした。

一人の開発者として、求められるクオリティのモノを作ることはできるというレベル感。

フルスタックになっていくとしても、得意技を持っておきたいと思う今日この頃。

100名以上くるイベントへCfPを出す

これは叶わず。

というかそんなイベントもなかったが。(ただの言い訳)

冬キャンプをしたいので貯金する

冬キャンプは叶わなかったが、貯金はした。結構貯めたと思う。

マネーフォワードで可視化してそれを眺めることでモチベーションにつなげたり、持株会を利用してそもそも手元に入ってこない形で資産形成していったのがよかったかな。

生活の質を一定に保つ

これはどうだろう?そもそも「普段の生活」の定義が揺れまくっていた2020年、それなりに健全な人間の生活はできていたとは思う。

他人に優しくする

これもだめだなあ。

余裕がなくなるとすぐ対人コミュニケーションが雑になってしまって良くない空気にしてしまう。特にオンラインのコミュニケーションだと、相手の気持ちを感じ取りづらいのでより丁寧なコミュニケーションをするべきだったのかもと思うことも多々。

人間的な成長をしないとだめですね。

まとめ

典型的に宣言だけしてやった気になる1年を過ごしてしまった。

コロナもあって環境の変化が激しかったというのはありつつも、怠けすぎた一年だった。

業務でGolangを書き始めた。最初の週にやったこと雑記 + (少しだけ)思ったこと

新卒2年目も終わろうとしております、もがみです。

ここまでフロントエンド一筋でReact Nativeを使ったアプリ開発とか、Elmを使ったWebフロントエンド開発をやってきましたが、運良くサーバーサイドにチャレンジする機会を得ることができました。チームのタスクがサーバーサイドに偏重している間のお助けとしてやっている部分も大きいので、いつまで続くかは現状未定ですが、せっかくなのでやったこととか、感じたことをだらだら書いておきます。

前提

  • Tour of goは一通りやった(全部完璧に理解しているとは言っていない)というレベル感。
  • 普段はTypeScriptとかElmとか書いているひと。フロント寄り。
  • サーバーサイドは学生の頃趣味で書いてたことがあるくらいの知識レベル。

環境構築

なにはともあれ環境がないと開発できないので環境構築をしました。 普段のフロントエンド開発はWebStormを使っているので、Golandを選択。とりあえず体験版を入れつつ、特に問題がなければ、WebStormと統合してIntellJ IDEA Ultimateのライセンスを購入しようかなと思います。

知らなかったんですがIntelliJJava系の言語のためのIDEというわけではなく、JetBrains系の様々なIDEの機能を内包しているようです。また、すでに持っているWebStormのライセンスをアップデートすることもできるらしい。いくらになるのかよくわかりませんが買うことになったら調べてみようと思います。

support.samuraism.com


Goland自体は公式から適当にダウンロードして起動すればOK。個人的必須プラグインであるIdeaVimもインストール。(大事)

システムにGoを入れないといけないので、普段使っているanyenvでgoenvを入れて、開発するプロジェクトのgo.modに書いてあったバージョンのGoをインストールした。

改めて、Golandでプロジェクトを立ち上げると go: github.com/example/example-repo@v1.6.0: reading github.com/example/example-repo/go.mod at revision v1.6.0: unknown revision v1.6.0 と言われた。(example/example-repoは実際には会社のプライベートリポジトリ)

これはプロジェクト内で使っているプライベートなライブラリが取得できずに怒っているっぽかった。SSH鍵は設定してあるしどうしたら…?と思い調べたら、GOPRIVATEという環境変数を指定してやる必要があるということがわかった。

syfm.hatenablog.com

他にもいくつかの記事をみて、

  • sumdbというGolangパッケージのチェックサムを返すサーバーがある
  • そいつのおかげでproxyからパッケージを取ってきても改竄が行われていないことを証明できるという仕組み
  • sumdbにチェックサムが存在しなかった時、proxyの他のサーバーにパッケージ名が流出しないように処理を止めるという仕組みがある
  • GOPROXYに指定したパッケージを取りに行くときはproxyではなくオリジン(GitHubとか)に直接取りに行くようになる

みたいな理解をした。多分あってる?

f:id:uutarou:20201220235949p:plain

Golandは設定画面からイイ感じに環境変数を入れられるので入れてあげた。(わかりづらいがインプットの右端にあるボタンを押すと、イイ感じに入力できるフォームみたいなのが出てくる。イイ感じに。)

さっきのエラーが出たリポジトリ以外にもいくつかのプライベートリポジトリに依存しているようだったのでワイルドカードで指定した。

ここまできたら、適当にソースコード開いて赤くなってるimport文の上でOpt + Enterを叩いてSync dependenciesみたいな項目を選ぶと依存ライブラリの取得が走る。規模にもよると思うが、ちょい時間かかった。

思ったこと

ポインタ

他言語からきて一番戸惑うのはやっぱりポインタのような気がする。理解はしているつもりだったがいくつか致命的なバグを埋め込みそうになった。

Goを始める僕に言いたいことがあるとすれば*&を使う時は慎重になれ」ということかなと。(Go普段から書いてる人からしたら当たり前体操だと思うが)

TypeScript(他、最近のモダン & 高機能な言語)になれていると、基本的にしっかり型が当ててあって、コンパイラが怒りさえしなければ動くのが普通だと思ってしまうところなんだが、Goも型がある言語という思いが先行して、ポインタは常にnilである可能性を含んでいるということを忘れて、IDEが怒っているからという理由だけで&やら*やらを書いて本当に痛い目をみた。(デプロイしてウキウキしながらリクエストを叩くと500が返り、Loggingにはpanicの5文字が。。。)

それを繰り返して、「nilをチェックするのはプログラマの責任なんだぜ」というメッセージに気づくことができた。

フロントエンドエンジニアにGoを書かせる機会があったら是非「TSでいえばasとか!とかああいうのを使う時と同じ気持ちでポインタは扱わないといけないものなんだ」ということを伝えてあげてほしい。(ちょっと意味合いとしては違うと思うが、ポインタとの正しい向き合い方に気づいた後の僕の気分としてはそんな感じだった)

1週間で理解できたこととしてはこんなところだが、いまだに関数の戻り値や引数にはポインタ型を使うべきなのか、そうではないのかというところの判断がつかない。飲みの席での話なので100%理解できていないと思うが、Goは言語の仕組み上、ある程度の大きさまでは値渡しの方がパフォーマンス的にも有利であると聞いた。コピーのコストをとってもパフォーマンスで勝つらしい。不思議だ。

文法が少ないのは良い

Goの良いところとして、1個のことやる書き方は1つしか存在しないというのがよく挙げられると思うが初学者にとってそれは本当に嬉しいことだと感じた。

Tour of goの知識があれば、使われているライブラリや、他のメンバーが書いたコードも時間をかけて読めば何をしているか理解するには十分であるというのはめっちゃいい。

テストもわけわからんテストフレームワークの使い方とか覚えなくてもいいし最高。


他にも思うところはあるんだが、社内事情がだんだん濃くなっていきそうなのでブログはこの辺で。

PrettierとLinterを共存させるときの公式オススメが変わったらしいという話

個人でやっているプロジェクトにPrettierとESLintを入れようとしたときの話。

ESLintにもコーディングスタイルを整えるための機能があるので、何も考えずにそのまま両方を入れると、お互いのルールが競合してしまうという問題が発生します。
これを防ぐために、Prettierの公式ドキュメントに共存させるためのオススメ手順が書いているのですが、最近この内容に少し変化があったっぽいということに偶然気がついたので記事にしておきます。


prettier.io

これが公式のドキュメント。書いてあることを雑に要約すると

  • コードフォーマットのことはPrettierを、コード品質に関することはLintを使ってくれ
  • ただLintツールのルールと競合することがあるからeslint-config-prettierのようなコンフィグを入れてくれ

と書いています。つまり、Linter側のコードフォーマットに関するルールを全て無効化して、その上でPrettierを使ってコードフォーマットをするというのが推奨されています。

blog.ojisan.io

(こちらの記事が大変わかりやすく参考になりました)

ここまでの話は昔から同じようなことが書いていたのですが、このあとの部分がここ最近変わったみたいです。

この記事を書いている時点のドキュメントを引用すると以下のような注意書きがあります。

When searching for both Prettier and your linter on the Internet you’ll probably find more related projects. These are generally not recommended, but can be useful in certain circumstances.

First, we have plugins that let you run Prettier as if it was a linter rule:

・eslint-plugin-prettier
・tslint-plugin-prettier
・stylelint-prettier
These plugins were especially useful when Prettier was new. By running Prettier inside your linters, you didn’t have to set up any new infrastructure and you could re-use your editor integrations for the linters. But these days you can run prettier --check . and most editors have Prettier support.

これまた雑に要約すると、

  • PrettierをLinterルールのように実行するツール(eslint-plugin-prettierなど)があるが、これはPrettierがまだ新しいツールだった時には役に立っていたが、現在はオススメしない。
  • なぜなら、現在はprettier --check .(dry run的なやつ)があったり、多くのエディターがPrettierをサポートしているからである

また、これらのツールを使うデメリットとして以下が挙げられています。

  • Linterとして実行すると、スタイルに関するエラーがエディター上で赤線で表示されたりする。Prettierはコーディングスタイルを忘れるためにあるのに。
  • 普通に実行するより遅い
  • 1レイヤー増えるので壊れるリスクが増える

こちらアーカイブを見てもわかるように、過去には公式でeslint-plugin-prettierのようなプラグインをオススメしていたみたいですが、公式オススメではなくなったっぽいという話でした。

ちなみに、LintかけたあとにPrettierをかける手段としてはprettier-eslintというのがリコメンドされていました。ちなみに、prettier-eslintはCLI機能を有していないので、CLIから実行したい人はprettier-eslint-cliというパッケージがまた別で用意されているのでそっちを使うといいかもです。

React Native 0.60以降でreact-native-vector-iconsが使えないときのはなし

久々にブログ書いてます。

相変わらず日々React Nativeを触ったりしています。RN界隈の人はご存知かと思いますが0.60以降のバージョンでは react-native link {ライブラリ名} のようなコマンドを実行することなく、ネイティブに依存するライブラリを使用することができるようになりました。

最近、React Native 0.62のプロジェクトでreact-native-vector-iconsを使おうとしたらiOS端末で Unrecognized font family と言われてしまって困ったのでメモ書き。

使えるようにするための手順だけ書くと

  • npm i react-native-vector-icons
  • Info.plistにフォントファイルを列挙

これだけでいけます。今日時点でREADMEには手動でプロジェクトに追加する方法/ react-native link を使う方法 / CocoaPodsを使う方法の3つが書いてますが、多分この方法が一番いいのかなと思います。(経験上手動でXcodeのプロジェクトをいじるのはRNのアップデートとかする時に辛いことになるのでできる限り避けた方がいい)

「Info.plistにフォントファイルを列挙」は具体的にはこう

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <!-- 中略 -->
        <!-- 以下追加 -->
    <key>UIAppFonts</key>
    <array>
        <string>AntDesign.ttf</string>
        <string>Entypo.ttf</string>
        <string>EvilIcons.ttf</string>
        <string>Feather.ttf</string>
        <string>FontAwesome.ttf</string>
        <string>FontAwesome5_Brands.ttf</string>
        <string>FontAwesome5_Regular.ttf</string>
        <string>FontAwesome5_Solid.ttf</string>
        <string>Foundation.ttf</string>
        <string>Ionicons.ttf</string>
        <string>MaterialCommunityIcons.ttf</string>
        <string>MaterialIcons.ttf</string>
        <string>Octicons.ttf</string>
        <string>SimpleLineIcons.ttf</string>
        <string>Zocial.ttf</string>
    </array>
        <!-- ここまで -->
</dict>
</plist>

こんな感じでフォントファイルを列挙すればOKです。生のInfo.plistいじるのが怖い場合はXcodeからいじると良き。

GWは巣篭もりしながらなんかRNでつくろーかなと思ってます。気の向くままに。

モノレポ on husky戦略

2020年は毎月2本技術的なアウトプットするぞ、と言いつつ1月から打ち破ってしまったので罪滅ぼしに最近業務でやったことについて備忘録的に書いておこうと思います。

モノレポを採用しているプロダクトにhuskyを導入したという話です。

※前置きが長いので、いらない人は本題のところまで読み飛ばしてくださいませ。


モノレポ

モノリシックレポジトリの略。

詳細な定義は知らないので、実際には違うかもしれないが(ていうか多分違う。)この記事におけるモノレポは「一つのGitリポジトリを用いて、一つのプロダクトのフロントエンド/バックエンド等のコードを管理するGit戦略」ということにして進めたいと思います。

husky

github.com

こいつのこと。これは一言で言うと、JS開発者間でGitフックの設定を揃えるための便利ツールとでも言えば良いのだろうか。

package.jsonに依存関係として定義しておくと、npm installnpm ciを使って依存ライブラリのダウンロードをした際に、Gitフックを設定してくれるという感じの挙動をします。あとはpackage.jsonや設定ファイルにgit commit時やgit push時にやらせたいことを列挙しておけば、自動的に設定に宣言したことを実行してくれます。

まぁJSを使って何かしらの開発をしていれば、npm installは間違いなくやるだろうし、そのタイミングを使ってGitフックが設定されるので実質的にGitフックの設定を強制することができるという素敵OSSってわけです。

これを使ってcommit前にPrettierやなんちゃらLintとかをかけておけば、誤ってコードフォーマットを忘れたコードがGitリポジトリ上に上がって、コードレビューで「フォーマッタかけてください」みたいな不毛なやりとりを産むこともないので素晴らしいのです。

ちなみに

ちなみに、huskyはGitフックを設定してくれるだけなので、lint-stagedというツールと組み合わせることで、ステージに上がっているファイルを対象に必要となるフォーマッタやLintをかけるということができます。どのファイルに対してどのフォーマッタを適用するかみたいなのを指定できるので超良い。超おすすめです。

んで本題。

基本的にここまでで書いたことをやりたかったら、一番早いのはlint-stagedのREAMDEに書いてある

$ mrm lint-staged

というのをプロジェクト直下で叩いてやるとインストールされているフォーマッタ等をみて良い感じにしてくれるので誰でもできると思います。

ところが、僕がメンバーとして関わっているプロダクトのリポジトリはモノレポ構成を取っており、1つのGitレポジトリの下にWebフロントエンド(ElmとTypeSript)やらスマートフォンアプリ(React Native製なのでTypeScript)、ほかにもサーバーサイドのコード等も入っているというような状況です。

このような状況において、huskyを導入するにはちょっと工夫をする必要があります。というのもGitフックはGitレポジトリ単位でしか設定できないので、たとえばWebフロントエンドのディレクトリ配下でhuskyを設定してしまうとスマートフォンアプリのディレクトリではhuskyを使うことができません。(できません、というか上書きで設定されるので最後にインストールしたものだけが生き残ってしまう)

huskyのREADMEにはlernaを使うように書いています。lernaはモノレポでの開発を支援するツールで、複数のnpm packageをスマートに管理してくれるものらしいです。(雑な理解)

ユースケースを見た所、npm packageの開発をモノレポを使って行う際に便利な機能を提供しているツールのようで、今回のようなケースには向かないと判断し別の方法を使ってなんとかhuskyを使う方法を模索しました。

そこで今回はGitリポジトリ直下にhusky用のディレクトリを作成し、そこで一括管理するという方法をとりました。

husky-example/
├── _husky
├── server
├── sp-client
└── web-client

こんな感じ。

_huskyディレクトリのしたのpackage.jsonのdevDependenciesにhuskyを追加し、設定を書きます。

今回Linterやformatterのツール自体はそれぞれのコードのあるディレクトリのpackage.jsonに書きたかったため、こんな感じで相対パスで指定するようにしました。

{
  "name": "husky",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "husky": "^4.2.1",
    "lint-staged": "^10.0.6"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "../sp-client/app/**/*.{ts,tsx}": "../sp-client/node_modules/.bin/tslint -p ../sp-client --fix"
  }
}

少々冗長な感じは否めないですが、まぁ仕方なし。。。

そしてもう一点やっておくとよいかもしれないのが、Lint対象のディレクトリにあるpackage.jsonpostinstallに_huskyディレクトリ以下でnpm ciを走らせるスクリプトを書くことです。

具体的にはこんな感じ。

{
  "name": "sp-client",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "postinstall": "cd ../_husky && npm ci"
  },
以下省略

このようにしておくことで、sp-client以下でnpm inpm ciをやったとき、一緒に_huskyディレクトリ以下でnpm ciが実行され、Gitフックが設定されます。

ここまで設定しておけば、普段の開発では意識することなくcommit時のlintが走ります。

おわりに

ということを先日仕込んでみました。まだマージされてから日が浅いので何か問題が起こったら、考え直そうと思っていますが、今のところは順調に動いています。

ここまでドヤ顔で記事を書きましたが、husky用のディレクトリを切ったらいいのではとか、postinstallを書いたらいいのではとかは全て開発メンバーのアイデアです。。。

それではまた。