末尾最適呼び出し化

東京のWebエンジニアの日記帳

CircleCIでコミットメッセージを取得する

前提

  • CircleCI 2.x

背景

本業でCIツールはCIrcleCIを使っているのですが、
werckerと違ってCircleCIは契約したコンテナ数しか使えないので、
開発チーム内の別プロジェクトがコンテナを占拠していると、その実行が終わるまで待たされることになります。

GithubのプッシュをフックにCI実行しているのですが、
適切にCIを実行するべき単位でプッシュするエンジニアだけなら問題はないのですが、
作業単位で適当にプッシュするエンジニアがいると、しかもそれが数コンテナ使うようなプロジェクトだと、
運が悪いと無意味な待ち時間が発生します。

一応その対策として、
Auto-cancel redundant buildsであったり、コミットメッセージに[skip ci]と書くことでCIを実行しない、という機能もあるのですが、
適当にプッシュするエンジニアがskip ciなんかしてくれないので、
より強制的にCI実行するかどうかを選択させる方法を考えました。

[skip ci] と書くことでCIをスキップ、ではなく、 [run ci] と書かないとCI実行されない、という方法です。

CircleCI上でコミットメッセージを取得

CircleCIは環境変数でいろんな値がとれますが、
参考: Using Environment Variables - CircleCI
コミットメッセージは取れません。

なのでややスクリプトを書く必要があります。

git log --format=oneline -n 1 $CIRCLE_SHA1

これでコミットメッセージは取得できます。
再利用したい場合は変数に格納とかするといいかもしれません。

[run ci] と書かないとCI実行しないスクリプト

上をふまえて、

- run:
    name: [run ci] がなければ止める
    command: |
      if git log --format=oneline -n 1 $CIRCLE_SHA1 | grep "[run ci]"; then
        echo "Run"
      else
        echo "Stop"
        exit 1
      fi

これでコミットメッセージにrun ci が含まれなければ exit 1するので実行を止めることができます。

ただ、これだとmasterへのマージコミットにも[run ci]を含めないと止まってしまうので、masterブランチの場合もCIを止めないようにします。

- run:
    name: [run ci] がなければ止める
    command: |
      if test $CIRCLE_BRANCH = "master"; then
        :
      elif git log --format=oneline -n 1 $CIRCLE_SHA1 | grep "[run ci]"; then
        echo "Run"
      else
        echo "Stop"
        exit 1
      fi

これで無事、CircleCIに平穏が訪れます。

チーム開発でgithubのリポジトリ運用について

2つの運用形態

僕は今本業でも副業でもGIthubを使ったチーム開発をしていますが、
Github上のリポジトリでチーム開発をする際、

  1. メンバーが各自リポジトリをフォークして、フォークしたリポジトリにブランチを切って本家に対してプルリクを出す(副業)
  2. メンバーがリポジトリにブランチを作る権限を持ち、ブランチをリポジトリ上に作ってプルリクを出す(本業)

の2つの運用ルールを経験しています。

本業も副業も企業のプロジェクトなのでもちろんプライベートなリポジトリです。
今回はそれぞれのルールにのメリット・デメリットについて書こうかなと思います。

1. フォークしたリポジトリからプルリクを出す

誰でも彼でもリポジトリにブランチを作成されては困るような、 OSS等の公開されたプロジェクトでよく取られる方法です。
hoge/fugauser/fugaにフォークしてプルリクの向ける先を hoge/fuga:masterにする、という運用ですね。

これをプライベートリポジトリで運用する際について。

メリット

  • 本家のリポジトリのブランチが増えない
    ブランチを作成するのはフォークした先のリポジトリなので、
    本家のリポジトリのブランチが増えません。
    メンバーが管理できるブランチは自分のリポジトリ上のブランチだけで、
    フォークしたリポジトリにブランチが増えてきたら適当に消しまくればいいだけです。 ブランチ管理が楽です。

  • 権限管理が楽
    フォーク先のリポジトリは好き勝手やらせて、
    本家のリポジトリではPR作成の最低限の権限だけで済みます。 force pushして本家リポジトリをぶっこわしたり、
    めちゃくちゃな作業をされても本家はノーダメージでいることが出来ます。
    メンバーがあまり信用できないとか()、初心者が多いとか、そういった事情があれば強力かもしれません。

デメリット

  • 本家master追従が面倒
    本家のmasterに追従するupstream等のブランチをつくっておき、
    本家masterが更新されたら、フォークされたリポジトリのローカルのmasterにupstreamをマージしなければなりません。
    これが結構手間です。

  • リポジトリを探すのが面倒
    自分が関わるリポジトリが本家とフォーク先の2つになります。
    フォーク先から本家へはリンクが表示されるので移動が簡単ですが、
    本家のリポジトリのissue等を見ているときに、自分のフォーク先リポジトリを見ようと思うと、
    検索するかブックマークから移動する等するしかなく、手間です。

  • 他のメンバーのブランチに手を出せない
    各フォーク先リポジトリのメンバーとして招待されていない場合(普通しない)、
    他の人のリポジトリを手元にプルしたり出来ません。
    微修正してあげる場合や、手元で動作確認する場合に非常に困ります。
    特にそのプルリクがマージされる前提で作業をするというような見切り発車ができなくなります。

僕は面倒なのでこの運用が好きじゃありません。
面倒なだけならまだいいですが、他の人の作業ブランチに手を出せないことで、
チーム開発の効率が微妙に下がるのがかなりいただけないですね。

ちなみに、githubの無料ユーザー(プライベートリポジトリを作成できない)でも、
フォークしたプライベートリポジトリはプライベートになるので、
公開状態について心配する必要はありません。

2. リポジトリに直接ブランチを切る

プライベートリポジトリだとこちらのルールで運用されることが多いのではないでしょうか。

僕はこちらの方が楽で好きです。

メリット

  • 他の人の作業ブランチに手を出せる
    ↑のデメリットで書いたようなことですが、
    全てのブランチに手が出せるのでもちろん手元にプルして動作確認してみたり、
    「ここ間違ってるから直しといたわ✌」ができます。

  • 1リポジトリで完結するので楽
    issue/PRみたりつくったり、ページ移動が少なくて済みます。

デメリット

  • ブランチが増える
    適当にブランチを作ったり、
    マージ後に消すみたいな運用を忘れると、
    途端に謎の消していいのかわからないブランチが増えます。 運用で気をつけたり度胸のある人がどこかのタイミングで古いブランチを全消しすれば済む話ではあります。

    f:id:nisokuyuki:20181012040127p:plain
    ↑本業で一番ブランチが浮いた状態で残っているリポジトリ

  • 権限管理
    適切に権限管理をする必要があります。
    「この人は初心者であまりわかってなさそうだから最低限の権限で、 この人は強いからマージ権限も与えて・・・・」とか管理が煩雑になります。 まあ、 信頼の置けるチームなら全員に全権限をあげちゃえばいいとおもいます。

おわり

どちらも良し悪しありますが、forkする形式だと機能性が損なわれて、そうでない形式だと運用が面倒になる、という感じです。
僕はforkするの面倒だし嫌いですが、それぞれ好みがあるんでしょう(適当)

FaaStRubyにマルチバイト文字使えるようになるかきいてみた。

RubyのFaaS

api.faastruby.io

AWSLambdaやCloudFunctionがRuby対応してくれないので、

このRubyのFaaS(Functions as a Service)のFaaStRuby、
結構好きで使ってるんですが(主にSlackBot用に)、
日本語が扱えないのが玉に瑕なので、
「マルチバイト対応予定あります?」と運営に投げてみた。

結果

f:id:nisokuyuki:20181008215819p:plain

(適当英語には目をつぶっていただけると・・)

要約

ぼく「日本語使いたいんだけど使えないわ。マルチバイト対応予定あります?」
運営「次のリリースで対応するね。ちなみにどんなリクエストとレスポンス?僕はKemalと関係があると思うけど」
ぼく「(Kemalとは関係ないと思うけどなあ・・・)こういうリクエスト投げたらこういうレスポンスだった。期待する出力はこれやで。よろしく〜」

ということでうまく行けば次回のリリースで多言語対応してくれるかも・・・?
期待しましょう。

railsのdocker開発環境をつくってて躓いたメモ

前提

扱っているプロジェクトが増えてきて、
また個人用のPCはLinuxにしていちいちそれぞれの開発環境を整えるのが面倒になってきたので、
docker-compose upさえすれば動く状態にしたかった。

いろいろ格闘したので備忘録

環境

  • docker for mac
  • docker-compose

dockerコンテナで使うもの

  • ruby 2.5.1
  • node vわすれた
  • mysql 5.7.9 (カラム名の最大長のアレの都合で。あと8はバグバグなので)

↑今回の話にあんまり関係ないかも

躓いたところ

1. docker for macおそすぎ問題

Docker for Macは遅いらしい。
ボリュームにマウントするあたりがかなりLinux版と比べて遅いらしい。

解決策:
Docker for Mac Edgeをインストールした。
Edge版はここのページ中盤のGet Docker CE for Mac(Edge)からダウンロードできる。

注意
既存のDocker for Macで作ったイメージとコンテナが全部消えるので、多少面倒。
消えるのか、認識されなくなってディスク上には残るのか知らないので一応ゴミファイルにならないように全消ししてからedge版を入れるのをオススメします。

2. .bundler をボリュームにマウントしようとするとちょっと面倒

ローカルにもdockerを使わずに生の開発環境を用意してしまった場合、

$ bundle install --path vendor/bundle

等すると、
.bundle/config

---
BUNDLE_PATH: "vendor/bundle"

が書き込まれてしまい、

これを↓みたいにインストールして docker-compose.ymlでマウントしていると、

docker/dockerfile.dev

RUN bundle check --path .bundle || bundle install -j4 --path .bundle

docker-compose.yml

  web:
    build:
      context: .
      dockerfile: ./docker/dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/rails_app
      - bundle:/rails_app/.bundle
      - /rails_app/.git
      - /rails_app/tmp
      - /rails_app/log
      - /rails_app/vendor
volumes:
  bundle:
    driver: local

bundlerのパス指定は vendor/bundleだけど実際には .bundleにインストールされているので困る。

解決策:

  • 1. docker環境はパス指定しない docker環境なんだしグローバルにインストールしてもいいよね、ということで。

  • 2. docker-compose.ymlでパス指定を無理やり変える RUN bundle installしている部分より下で、

RUN sed -ie "/BUNDLE_PATH:/d" .bundle/config &&\
    echo BUNDLE_PATH: \"vendor/bundle\" >> .bundle/config

を書けば、
.bundle/configの内容が置換されるので一応の解決にはなる。

のどちらか。
あんまりスマートではない感じ。 envでBUNDLE_APP_PATH指定するとうまいこといくという情報も見たけどなぜか自分の環境ではうまく行かなかったので。

RailsのRspecでresponse.statusはデフォルトで200らしい

環境

なんかみつけた

Railsの仕様なのかRspecの仕様なのか知らないけど、

describe '何これバグじゃね' do
  it { expect(response.status).to eq 200 }
end

これが何故か通る。

なので、request specしてる風を醸し出す裏技ができる

describe 'GET /hogehoge' do
  subject { get '/hogehoge' }
  it do
    expect(response.status).to eq 200
  end
end

こういうテストをいつでもどこでも通せる。

本当にテストする場合は

describe 'GET /hogehoge' do
  subject { get '/hogehoge' }
  it do
    subject
    expect(response.status).to eq 200
  end
end

小規模メディアを乱発する小規模WEBベンチャーの何が(開発的に)面白くないか

注意書き

以下に書くことは新卒1年目のバックエンドエンジニアが考えることであって、
小規模メディアといえど複数のある程度賢いはずの大人が一生懸命考えて事業にしているものなので、
当然多分に誤解や狭い知見が含まれていることをご了承ください。

僕の働く環境

前提として僕の働く会社の事業について軽く触れておきます。

僕が本業で働く会社では、
SEO的に勝てそうな分野に対してハイエナ的にWEBメディアを立ち上げるという事業をしています。
コンテンツのジャンルは多岐にわたっていて、「この分野の社会課題を解決しよう」というような信念は無いように見えます。

例えば、労働市場に対して問題意識があって立ち上がったベンチャーであれば、

  • 新卒の就活メディア
  • 中途向けメディア
  • フリーランス向けメディア -人事向けのメディア
  • ・・・

という横展開をしていくのはとても筋が通った話だと思いますが、
弊社は金融・美容・恋愛...等のジャンルのコラム型メディアを打ち出しています。

この信念の無さというか、事業の方向性についての非難についてはまた別の記事で書くとして、
今回は、こういう業態についての話だということをご理解いただければ。

WEBメディアの開発

まず、これを言ってしまうと「なぜそんなところに就職したの?」というマサカリが飛んできそうですが、
WEBメディアの開発は難しくありません。
そして、やることはジャンルにかかわらずほとんど一緒なので技術的に真新しい物も特にありません。

メディアの開発で最も時間を使うのは、SEO対策です。
SEO対策というワードの正しさについては議論しないものとする...笑

Googleのクローラフレンドリーなサイトになるようにサイト構造の見直ししたり、内部リンクがどうのとか、パンくずリスト作ったり、セマンティクスがどうとか、リッチスニペットがどうとか、そのへんのことに一番の時間を使います。  

また、新規機能開発についても、検索軸を増やすようなものばかりです。
例えば、コラムであれば「新着順に表示」「カテゴリから探す」「関連ワードから探す」といった機能を作っていきます。
コンテンツや機能とは関係のない、Google検索で引っかかりやすくするためのものなので、これもSEO対策の一種といえるでしょう。

運用の方々は、
オーガニック流入(検索からのアクセス数)の増減や、
あるキーワードでの検索順位が上がった下がった、という話は面白いのかもしれませんが、
エンジニア的には全くおもしろくないです。

なぜなら優秀な設計やプログラムであるほどアクセスを集めるわけではないからです。
(speed updateの話はしないものとする)
バックエンドを新人エンジニアが書いても、シニアエンジニアが書いても表に表示されるものが同じなら検索順位は変わりません。

こういった状況でプログラムの設計を考えることは、単に今後の開発効率のためになります。
いわゆる「変更に強いプログラム」を考えるだけです。
もちろんこれはエンジニア的には面白いですが、逆に言うと開発の面白さはこれだけです。
他のどんなWEBメディア以外のプロダクトであっても変更に強い設計をするのはあたりまえです。

と、いうようにWEBメディアの開発では一般的なプログラミングで得られる面白さの最低限のものしか得られません。

ユーザビリティ

SEOに関する開発がなくなったら、次に優先度の高いものとして、ユーザビリティを向上するための開発があります。
これは、サイト滞在時間や回遊率を上げるためのSEOチックな側面のあるものと、運用の社員が使うための管理画面の機能開発がありますが、
こと小規模ベンチャーにおいては(少なくとも弊社においては)まともにUI/UXについて議論する人がいないので、
「多分こうしたら便利なんじゃない?やってみて」という仮説を元に実装をします。

技術で食ってないベンチャーにおいては開発は運用の奴隷なので、こういった開発依頼がままあります。
「というか僕はフロントエンドエンジニアじゃねーよ」という依頼も来ます。
小規模ベンチャーだと人材リソースが足りなかったり偏ったりするので、バックエンド/フロントエンドの選り好みは簡単ではありません。
かくして僕はフロントもバックも書かされているわけですが・・・話がそれてしまいました。

エビデンスのない謎の仮説を元にした依頼を脳死しながら実装するのはとても気持ちがいいです。
皆さんも経験していただきたい。

以上のような開発をいっぱいしているので、さぞ弊社のメディアはユーザビリティが高いのだろうなあ(他人事)

さて

思いついた順にいろいろ書いてまとまりがなくなってしまいましたが、
なんとなく面白く無さが伝わって、一人でもWEBメディア乱発系ベンチャーに就職する方が減れば幸いです。

書こうかなと思って文章中にうまく書けなかったことを羅列します

  • 開発がどんなに頑張っても運用やライターが炎上させればそのメディアは潰されます。
  • メディア毎にデティールが異なっていて共通化できる部分はごく限られているので最近話題のマイクロサービスアーキテクチャというのもなかなか難しいものがあります
  • Webメディア開発しているとSEOに詳しくなれますが、SEOの答えはGoogleしか知りえないので輪郭を撫でることができるようになるだけです
  • 基本的にアフィリエイトサイトなので個人で作るものと何も変わりません。というか一人でメディア作ることもあります。会社に所属する意味がわからなくなるときがあります。
  • 得てして企業のHPにはそういう卑しいアフィリエイトサイトを運用していることは載っていないことが多いのでHP上からはわかりません。何してるかわかりにくいWEBベンチャーには気をつけましょう。

以上!