CircleCIでmix deps.getしたときに "Could not find Hex" とか言われて怒られたときの話

CircleCIの管理画面でGithubのProjectを追加して、次の画面で出てくる config.yml をなにも考えずにコピペしてイメージのタグを1.5にしたら言われた

どうしたら解決するの?

CircleCIドキュメントの Language Guide: Elixir にあるように、 mix deps.get の前に、下記を追加しました

      - run: mix local.hex --force
      - run: mix local.rebar

参考

circleci.com

感想

無いから入れた 言ってしまえばそれだけの記事になってしまった

CircleCIでプロジェクトを追加したときに出る config.yml のサンプルは1.4だったから問題なかったのかもしれない

最初からCircleCIドキュメントの Language Guide: Elixir からコピペしたらよかった

以上

お徳用マスクを買った

11月上旬頃に風邪を引いてから長引いていて、なかなかマスクが外せない

60枚入りのお徳用マスクを買っていたのだけど、使い切ってしまった

置いておいて腐るわけでもないから、多めにあっても困ることはないと思ってお徳用を探した。

ユニチャーム ソフトーク 超立体マスク  100枚入

ユニチャーム ソフトーク 超立体マスク 100枚入

これを買った

普通のサージカルマスクは、鼻の所を曲げるのが手間だったのだけど、今回買ったやつはその手間が省けるから良い。

以上

ex_docを初めて動かした 2017-12-14

ex_docというドキュメントツールがある

僕は今日初めて動かしたのでメモしておく

雑に書く

前準備

mix new する

mix new ex_doc_sample

やったこと

mix.exs を変える

こう変える

defmodule ExDocSample.Mixfile do
  use Mix.Project

  def project do
    [
      app: :ex_doc_sample,
      version: "0.1.0",
      elixir: "~> 1.5",
      start_permanent: Mix.env == :prod,
      deps: deps(),

      name: "ExDocSample",
      source_url: "https://github.com/kytiken/ex_doc_sample",
      homepage_url: "https://github.com/kytiken/ex_doc_sample",
      docs: [main: "ExDocSample", # The main page in the docs
             logo: "./logo.png",
             extras: ["README.md"]]
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      {:ex_doc, "~> 0.16", only: :dev, runtime: false}
    ]
  end
end
対象のモジュール

mix new した直後のモジュール

defmodule ExDocSample do
  @moduledoc """
  Documentation for ExDocSample.
  """

  @doc """
  Hello world.

  ## Examples

      iex> ExDocSample.hello
      :world

  """
  def hello do
    :world
  end
end
deps.get
mix deps.get
ドキュメントを生成
mix docs
確認

f:id:kytiken:20171214202149p:plain

できたー

感想

簡単にきれいなドキュメント作れる ex_doc 最高だな

参考URL

github.com

Phantom.jsがメンテナンス終了になる件を調べてみた

はじめに

Phantom.jsというヘッドレスなブラウザがメンテナンス終了になるという話を今年の4月ごろ聞いた。

いま仕事でやっているプロジェクトはRailsで、E2EテストをCapybara+Poltergeistで回している。 PoltergeistっていうのはPhantom.jsをCapybaraで動かすためのdriverなので、Phantom.jsがメンテナンス終了になるんだったらPoltergeistから乗り換えないといけないという話をしていた。

ただ自分の中で「なぜメンテナンスをやめてしまうのか」ということについて、興味が湧いてきたので調べてみることにしたというのが発端。

ソース

ソースはこのディスカッション

https://groups.google.com/forum/#!topic/phantomjs/9aI5d-LDuNE

Hi,

I want to make an announcement.

Headless Chrome is coming - https://www.chromestatus.com/features/5678767817097216
(https://news.ycombinator.com/item?id=14101233)

I think people will switch to it, eventually. Chrome is faster and more stable than PhantomJS. And it doesn't eat memory like crazy.

I don't see any future in developing PhantomJS. Developing PhantomJS 2 and 2.5 as a single developer is a bloody hell.
Even with recently released 2.5 Beta version with new and shiny QtWebKit, I can't physically support all 3 platforms at once (I even bought the Mac for that!). We have no support.
From now, I am stepping down as maintainer. If someone wants to continue - feel free to reach me.


I want to give credits to Ariya, James and Ivan! It was the pleasure to work with you. Cheers!
I also want to say thanks to all people who supported and tried to help us. Thank you!

With regards,
Vitaly.

以下意訳

やぁ

アナウンスメントしようと思う

Headless Chromeが来ました

最終的には、人々はChromeに乗り換えるようになると思う。Chromeは早くてPhantomJSより安定している。そして狂ったようにメモリを食わない。

私は開発中のPhantomJSに未来を見ない。PhantomJS 2と2.5を一人で開発するのは、もう限界だ。
さらに最近リリースした2.5 Betaには新しくてピカピカのQtWebKitがついているが、3つのプラットフォームをいっぺんにサポートすることは物理的にできない(私はそのためにMacを買いさえしたのに!)私たちにはサポートが無い。
今から私はメンテナーを辞める。もし続けたい人がいれば連絡してくれ。

私はAriyaとJamesとIvanに賞賛を贈りたいと思う。あなたと仕事ができてよかった。乾杯!
私はサポートをしてくれたり、私達を助けてくれたすべての人たちに対して感謝を言いたい。ありがとう!

敬具
Vitaly.

日本語に意訳してみたら、かなりつらそうな気持ちが伝わってきた(小並感)

Poltergeistメンテナーの反応

それと この件についてPoltergeistのリポジトリでIssueが上がっていた

github.com

It doesn't surprise me. With the number of people repeatedly posting issues (and demanding fixes with no contribution) in the PhantomJS repo, that were obviously issues with Webkit, trying to develop/maintain PhantomJS was probably a nightmare. There has been no work done in Poltergeist with regards to Headless Chrome, however once it is released it should be usable through selenium.

以下、超意訳

それについて私は驚いてない。
多くの人々がPhantomJSのリポジトリの中で何度もIssueを投稿していた(コントリビュートすることなしに修正を要求し続けてきた)
それはWebkitによる明らかな問題で、PhantomJSを開発・メンテナンスするのは悪夢だ
Headless Chromeに関するPoltergeistの仕事は無かったが、一方ではHeadlessChromeがリリースされるとSeleniumによって使うことができるはずだ

QtWebKitの問題

この問題の背景を知るのにこの記事がすごく参考になった

jser.info

以下、引用します

PhantomJSはQtWebKitを利用しており、QtWebKitは一度削除され最近また復活しています。
QtWebKitのAlternativeとしてChromiumベースのQtWebEngineが提供されています。

(中略)

どちらを利用した場合もPhantomJSには大量の修正が必要となり、サポートコストが高いためPhantomJSのメンテナンスを終了するとのことです。

感想

  • OSSの開発って大変だなぁと思いました(小並感)
  • QtWebKitの先行きもどうなるかわからないというのも一因なんだと思った
  • Headless Chromeで今のプロジェクトのテストを通してみたのだけど、何個か通ってないテストがあるし、実行速度は正直HeadlessChromeのほうが遅いという問題があるのだけど、乗り換えたいなぁと思った

自分の英語力がゴミなので、間違っている部分があればコメントなどで指摘いただければ助かります

ここ数日やっていること

ここ数日RabbitMQとrabbitmq-delayed-message-exchangeとElixirを組み合わせたサンプルを書くということをしている

ようやっとサンプルが動いたのでブログを書く

やりたいこと

github.com

やりたいことは、このPluginを使って指定した秒数待ったあとにメッセージを送るというもの

ここのReadmeに使い方が書いてあるのだけど、初心者の僕には、Javaっぽい言語のソースコードが載っているだけでイマイチピンと来なかった。

この記事ではRabbitMQでdelayed-message-exchangeのpluginだけインストールした状態から、実際にElixirでメッセージ送信するためにやったことを紹介する。

Exchange設定

f:id:kytiken:20171114215537p:plain

こんな感じにRabbitMQ managementからExchangeを設定する。

Elixirプログラム

受信側

defmodule Consumer do
  use GenServer
  use AMQP

  def start_link do
    GenServer.start_link(__MODULE__, [], [])
  end

  @exchange    "my-exchange"
  @queue       "gen_server_test_queue"
  @queue_error "#{@queue}_error"

  def init(_opts) do
    {:ok, conn} = Connection.open
    {:ok, chan} = Channel.open(conn)
    # Limit unacknowledged messages to 10
    Basic.qos(chan, prefetch_count: 10)
    Queue.declare(chan, @queue_error, durable: true)
    # Messages that cannot be delivered to any consumer in the main queue will be routed to the error queue
    Queue.declare(chan, @queue, durable: true,
                                arguments: [{"x-dead-letter-exchange", :longstr, ""},
                                            {"x-dead-letter-routing-key", :longstr, @queue_error}])
    Queue.bind(chan, @queue, @exchange)
    # Register the GenServer process as a consumer
    {:ok, _consumer_tag} = Basic.consume(chan, @queue)
    {:ok, chan}
  end

  # Confirmation sent by the broker after registering this process as a consumer
  def handle_info({:basic_consume_ok, %{consumer_tag: consumer_tag}}, chan) do
    {:noreply, chan}
  end

  # Sent by the broker when the consumer is unexpectedly cancelled (such as after a queue deletion)
  def handle_info({:basic_cancel, %{consumer_tag: consumer_tag}}, chan) do
    {:stop, :normal, chan}
  end

  # Confirmation sent by the broker to the consumer process after a Basic.cancel
  def handle_info({:basic_cancel_ok, %{consumer_tag: consumer_tag}}, chan) do
    {:noreply, chan}
  end

  def handle_info({:basic_deliver, payload, %{delivery_tag: tag, redelivered: redelivered}}, chan) do
    spawn fn -> consume(chan, tag, redelivered, payload) end
    {:noreply, chan}
  end

  defp consume(channel, tag, redelivered, payload) do
    number = String.to_integer(payload)
    if number <= 10 do
      Basic.ack channel, tag
      IO.puts "Consumed a #{number}."
    else
      Basic.reject channel, tag, requeue: false
      IO.puts "#{number} is too big and was rejected."
    end

  rescue
    # Requeue unless it's a redelivered message.
    # This means we will retry consuming a message once in case of exception
    # before we give up and have it moved to the error queue
    #
    # You might also want to catch :exit signal in production code.
    # Make sure you call ack, nack or reject otherwise comsumer will stop
    # receiving messages.
    exception ->
      Basic.reject channel, tag, requeue: not redelivered
      IO.puts "Error converting #{payload} to integer"
  end
end

送信側

defmodule RabbitmqTutorials do
  @queue       "gen_server_test_queue"
  @exchange    "my-exchange"
  def hello do
    {:ok, connection} = AMQP.Connection.open
    {:ok, channel} = AMQP.Channel.open(connection)
    AMQP.Basic.publish(channel, @exchange, "", "5", headers: [{"x-delay", 5000}])
    AMQP.Connection.close(connection)
  end
end

あくまでサンプルなので、ここからもっと洗練する必要がある

今回の目的は「サンプルを書いて動かしてみる」なので以上

躓いた所

AMQP

RabbitMQではAMQPというプロトコルを使用するのですが、中途半端な理解なまま先に進んだためわけわからなくなった。

AMQPについてはGreeの技術記事がすごくわかりやすかった

AMQPによるメッセージング | GREE Engineers' Blog

AMQP.Exchange#declare/4

ElixirのAMQPクライアントには AMQP.Exchange#declare/4 という関数があって、これを使えばExchangeを登録できる

AMQP.Exchange – amqp v0.3.0

delayed-message-exchange を使うには typeに x-delayed-message を入れないといけない AMQP.Exchange#declare/4 のtypeはAtomを入れるようになっていて x-delayed-messageAtomで書きたいんだけど、 - が付いているもんだからAtomが作れない

仕方がないからRabbitMQ managementで登録することにした

RabbitMQ management exchange登録

なんもわからずに登録しようとしたらこんなこと言われた

f:id:kytiken:20171114222539p:plain

406 PRECONDITION_FAILED - Invalid argument, 'x-delayed-type' must be an existing exchange type 

argumentsx-delayed-type 入れろとのこと

感想

ちゃんと理解してから先に進むの大事だなと思った

今日やったこと(2017/11/07)

今日はRabbitMQをElixirで使うために入門をしていた

まず公式チュートリアルを進める

https://www.rabbitmq.com/tutorials/tutorial-one-elixir.html

amqpを使用する

https://hexdocs.pm/amqp/readme.html

ちなみにチュートリアルでは 0.2.1 なので古い 僕は今のところ Sending まで進めたが、動かないということはなかった

外部から接続したかったため、 open/1 のドキュメントを読む https://hexdocs.pm/amqp/AMQP.Connection.html#open/1

雑なサンプルを置いておく

defmodule RabbitmqTutorials do
  def hello do
    {:ok, connection} = AMQP.Connection.open(host: "localhost",
                                             port: 5672,
                                             username: "kytiken",
                                             password: "hogefugapiyo",
    )
    {:ok, channel} = AMQP.Channel.open(connection)
    AMQP.Queue.declare(channel, "hello")
    AMQP.Basic.publish(channel, "", "hello", "Hello World!")
    IO.puts " [x] Sent 'Hello World!'"
    AMQP.Connection.close(connection)
  end
end

ここまでやったところで時間切れ

お疲れ様でした

今日やったこと

RabbitMQにrabbitmq-delayed-message-exchangeを入れるというのをやりました

やった方法を簡単にまとめます

方法

Dockerを使って試します

1.RabbitMQのコンテナを作ります

docker run --rm -p 15672 --name some-rabbit -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password rabbitmq:3-management

2.コンテナの中に入ります

docker exec -it some-rabbit bash

3.rabbitmq-delayed-message-exchangeのファイルを取ってきます

apt-get update && apt-get install -y curl
curl -O http://www.rabbitmq.com/community-plugins/v3.6.x/rabbitmq_delayed_message_exchange-0.0.1.ez 

4.pluginsフォルダにぶち込みます

mv rabbitmq_delayed_message_exchange-0.0.1.ez $RABBITMQ_HOME/plugins

5.有効にします

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

以上