zakihayaメモ

RubyとRailsのことが中心

リポジトリをGitHubに変更してCircle CIで自動デプロイしようとしたら大ハマリした

今更ながらCircle CIを使ってみる

前から興味があったCircle CIでの自動デプロイを、機会があったので試してみました。
リポジトリをBitbucketからGitHubに変えて、Circle CIに登録してデプロイできるようになるまで8時間。。。
いい経験にはなりましたが大変でした。

構成

サービス稼働中のRailsアプリに対して、デプロイ方法のみを変更します。

変更前

変更後

作業内容

circle.yml追加

# circle.yml

machine:
  timezone: Asia/Tokyo
  ruby:
    version: 2.1.7
deployment:
  master:
    branch: master
    commands:
      - bundle exec cap production deploy
      - bundle exec cap production deploy:restart

リポジトリの向き先を変更

$ git remote set-url origin git@github.com:zakihaya/sample_repo.git

GitHubにpushする

$ git push origin master

Circle CIにプロジェクト追加

普通に登録する。
その後、サーバにSSHで接続するための秘密鍵を追加しておく。

Project settings → Permissions → SSH Permissions

リポジトリを修正するところは1箇所ではなかった!

これで適当な修正を加えてpushしてみると、こんな感じのSSHエラーが。。

(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@example.com: git exit status: 128
git stdout: Permission denied (publickey).

この調査で3時間くらいハマる。

やってみた事は

どれをやってもうまくいかない。。。

困ったのでもう一度最初からエラーを見直してみる。

+ bundle exec cap production deploy
DEBUG [1562897d] Running /usr/bin/env [ -d ~/.rbenv/versions/2.1.7 ] as deploy@example.com
DEBUG [1562897d] Command: [ -d ~/.rbenv/versions/2.1.7 ]
DEBUG [1562897d] Finished in 2.463 seconds with exit status 0 (successful).
INFO [addbc921] Running /usr/bin/env mkdir -p /tmp/sample-repo/ as deploy@example.com
DEBUG [addbc921] Command: ( RBENV_ROOT=~/.rbenv RBENV_VERSION=2.1.7 /usr/bin/env mkdir -p /tmp/sample-repo/ )
INFO [addbc921] Finished in 0.386 seconds with exit status 0 (successful).
DEBUG Uploading /tmp/sample-repo/git-ssh.sh 0.0%
INFO Uploading /tmp/sample-repo/git-ssh.sh 100.0%
INFO [edd74d42] Running /usr/bin/env chmod +x /tmp/sample-repo/git-ssh.sh as deploy@example.com
DEBUG [edd74d42] Command: ( RBENV_ROOT=~/.rbenv RBENV_VERSION=2.1.7 /usr/bin/env chmod +x /tmp/sample-repo/git-ssh.sh )
INFO [edd74d42] Finished in 0.384 seconds with exit status 0 (successful).
INFO [0101ca3e] Running /usr/bin/env git ls-remote --heads git@bitbucket.org:zakihaya/sample-repo.git as deploy@example.com
DEBUG [0101ca3e] Command: ( RBENV_ROOT=~/.rbenv RBENV_VERSION=2.1.7 GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/sample-repo/git-ssh.sh /usr/bin/env git ls-remote --heads git@bitbucket.org:zakihaya/sample-repo.git )
DEBUG [0101ca3e]    Permission denied (publickey).

なんと、 bitbucket が残っているではないか!
Circle CIがソースを取りに行っている先がBitbucketのままになっているっぽい。

確認したところ、 config/deploy.rbrepo_url がBitbucketのままになっていた。。。

がっかりしながら修正

# config/deploy.rb

# set :repo_url, 'git@bitbucket.org:zakihaya/sample-repo.git' ← 削除
set :repo_url, 'git@github.com:zakihaya/sample_repo.git'

リポジトリを修正するところは2箇所ではなかった!

これでイケるだろうと思いきや、もう1つハードルが。

今度はこんなエラーが出る。

DEBUG [83c5bd28] Command: if test ! -d /var/rails/sample-repo/repo; then echo "Directory does not exist '/var/rails/sample-repo/repo'" 1>&2; false; fi
DEBUG [83c5bd28] Finished in 0.358 seconds with exit status 0 (successful).
INFO [72e69c47] Running /usr/bin/env git remote update as deploy@example.com
DEBUG [72e69c47] Command: cd /var/rails/sample-repo/repo && ( RBENV_ROOT=~/.rbenv RBENV_VERSION=2.1.7 GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/sample-repo/git-ssh.sh /usr/bin/env git remote update )
DEBUG [72e69c47]    Fetching origin

DEBUG [72e69c47]    Permission denied (publickey).


DEBUG [72e69c47]    fatal: The remote end hung up unexpectedly

DEBUG [72e69c47]    error: Could not fetch origin

(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@example.com: git exit status: 1
git stdout: Fetching origin

Permission denied (publickey).


fatal: The remote end hung up unexpectedly

今度はちゃんとエラーを見ると、サーバで git remote update をする時にエラーになっているっぽい。
なるほど、サーバ側のリポジトリの設定にもBitbucketが残っていたのか。。

/var/rails/sample-repo にデプロイしていたので、
サーバにログインして、リポジトリの向き先を変える。

$ cd /var/rails/remple-repo/repo
$ git remote set-url origin git@github.com:zakihaya/sample_repo.git

これで全てうまくいきました。

まとめ

Capistranoを使っていて、CIする直前にリポジトリを変えた場合は気をつけましょう。

haml使用時に値が無い属性(itemscope)などを追加する

<div itemscope>・・・</div>

のような感じで表示させたい場合は

%div{ itemscope: true }

とすればOK。

RubyのバッチでGoogleAnalyticsのデータを取得する(OAuth2利用)

Garbが使えなくなった・・・

もともとはGarbというgemを使ってアクセス数を取得するバッチを作っていたのですが、急にGoogleにログインができなくなってしまいました。。

Sija/garb · GitHub

いろいろと見てみると、Google APIがOAuth2以外の認証を受け付けなくなってしまった模様。
GarbでOAuth2を使うためにいろいろ試してみたのですがうまくいかず、Google謹製のgemに乗り換えました。

乗り換えたgemはこちら
google/google-api-ruby-client · GitHub

Google Developers Consoleで鍵ファイルを取得

まず準備として、Google Developers Consoleにアクセス。 https://console.developers.google.com

プロジェクトを作成していない場合は作成する。

プロジェクトを開いたら、
 APIと認証 > 認証情報
を開き、新しいクライアントIDを作成する。
アプリケーションの種類は、「サービス アカウント」を選択する。

IDを作成したら、「新しい P12 キーを生成」をクリックして、鍵ファイルをダウンロードする。

作成したクライアントのメールアドレスに権限を設定する

Analyticsのデータが見れるように、権限を設定します。
自分はこれを忘れてだいぶハマりました。

Google Analyticsを開き、「アナリティクス設定」を選択します。

アカウント→プロパティ→ビュー の順に、取得したいサイトの項目を選択していきます。

ビューの下の「ユーザー管理」をクリックし、Google Developers ConsoleのクライアントIDの下に表示されているメールアドレスを追加してください。
権限は、自分は編集までにしましたが、もっと低くても大丈夫かもしれません。

この時、ビュー設定の「ビューID」をコピっておきます。
後で使用します。

Gemfile設定

# Gemfile

gem 'google-api-client'
gem 'signet'

を記入して、 bundle install する。

OAuth2認証

client = Google::APIClient.new(:application_name => '')
# 鍵ファイルの場所を指定
key = Google::APIClient::PKCS12.load_key(File.join(Rails.root, 'certificates', 'analytics.p12'), 'notasecret')
client.authorization = Signet::OAuth2::Client.new(
  :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
  :audience             => 'https://accounts.google.com/o/oauth2/token',
  :scope                => 'https://www.googleapis.com/auth/analytics',
  :issuer               => 'example@example.com',    # Google Developers Consoleに表示されているメールアドレスを入力
  :signing_key          => key,
)
client.authorization.fetch_access_token!

今回は鍵ファイルを、 RAILS_ROOT/certificates/analytics.p12 に配置しています。
鍵ファイルの場所は各自書き換えてください。

client.authorizationのパラメータ issuer には、先程権限を追加したメールアドレスを入力してください。

Analytcsからデータを取得する

start_date = Date.today - 7
end_date = Date.today - 1

api_method = client.discovered_api('analytics','v3').data.ga.get
results = client.execute(:api_method => api_method, :parameters => {
  'ids'        => 'ga:999999',    # ビューIDを設定する
  'start-date' => start_date.to_s,
  'end-date'   => end_date.to_s,
  'dimensions' => 'ga:pagePath',
  'metrics'    => 'ga:pageviews'
}).data.rows

パラメータ ids には、権限追加時にコピーしておいたビューIDを入力します。

これを実行すると、

results
=> [["/", 10], ["page1.html", "5"], ["page2.html"], "8"]

のような形式でページビューが取得できます。

全体のソース

require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/installed_app'

module Google
  class Analytics
    def self.import
      start_date = Date.today - 7
      end_date = Date.today - 1

      # OAuth2認証
      client = Google::APIClient.new(:application_name => '')
      key = Google::APIClient::PKCS12.load_key(File.join(Rails.root, 'certificates', 'analytics.p12'), 'notasecret')
      client.authorization = Signet::OAuth2::Client.new(
        :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
        :audience             => 'https://accounts.google.com/o/oauth2/token',
        :scope                => 'https://www.googleapis.com/auth/analytics',
        :issuer               => 'example@example.com',
        :signing_key          => key,
      )
      client.authorization.fetch_access_token!

      # Analyticsからデータ取得
      api_method = client.discovered_api('analytics','v3').data.ga.get
      results = client.execute(:api_method => api_method, :parameters => {
        'ids'        => 'ga:999999',
        'start-date' => start_date.to_s,
        'end-date'   => end_date.to_s,
        'dimensions' => 'ga:pagePath',
        'metrics'    => 'ga:pageviews'
      }).data.rows

      # resultsからデータを取得する処理
      #  ・
      #  ・
      #  ・

    end
  end
end

Railsでメールを送信する機能を実装する時はmailcatcherを使うと便利

Webアプリを作っていると、メールを送信する機能を作ることがよくあるかと思います。 そんな時はmailcatcherというgemを使うと、仮想SMTPのような形で使えるので便利です。

準備

Gemfileを更新して、 bundle install します。

# Gemfile
group :development do
  gem 'mailcatcher'
end

設定ファイル記述

# config/environments/development.rb

Rails.application.configure do
 ・
 ・
 ・
  # 開発時のメール関連はMailCatcherを利用
  # RAILS_ROOTでbundle exec mailcatcherを実行し、http://127.0.0.1:1080/でメールが確認できる
  config.action_mailer.default_url_options = { host: 'localhost:3000' }
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
 ・
 ・
 ・
end

送信されたメールを確認する

mailcatcherを起動します。

$ bundle exec mailcatcher
Starting MailCatcher
==> smtp://127.0.0.1:1025
==> http://127.0.0.1:1080

起動したら http://127.0.0.1:1080/ にアクセスすると、下のような画面が表示されます。

f:id:zakihaya:20141208110133p:plain

確認する

今回はconsoleからメールを送信して確認してみます。

# メール本文
mail = Mail.new do
  from    'test-from@example.net'
  to      'test-to@example.net'
  subject 'Test mail using mailcatcher'
  body    'There is a test mail.'
end

# SMTPの設定。ActionMailerに設定したのと同じもの。
mail.delivery_method :smtp, { :address => "localhost", :port => 1025 }

# メール送信
mail.deliver!

http://127.0.0.1:1080/にアクセスすると、メールが届いています。

f:id:zakihaya:20141208111138p:plain

あら便利。

bundlerを使ってGemを作成する

Gemの作り方を勉強するために、どうしようもないGemを作ってみます。
テストを作成し、Travis CIで自動テストができることが目標です。

ソースはこちら
zakihaya/hello-zakihaya · GitHub

Gemを作成する準備

Gemの雛形を作成する

$ bundle gem hello_zakihaya

gemspecファイルを編集する

# hello_zakihaya.gemspec

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'hello_zakihaya/version'

Gem::Specification.new do |spec|
  spec.name          = "hello_zakihaya"
  spec.version       = HelloZakihaya::VERSION
  spec.authors       = ["hayazaki"]
  spec.email         = ["toru.hayazaki@gmail.com"]
  spec.description   = %q{greeting with zakihaya}
  spec.summary       = %q{You can greet to zakihaya with modification whatever you want}
  spec.homepage      = "https://github.com/zakihaya/hello-zakihaya"
  spec.license       = "MIT"

  spec.files         = `git ls-files`.split($/)
  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.3"
  spec.add_development_dependency "rake"
  spec.add_development_dependency "rspec"
  spec.add_dependency "activesupport"
end

編集したのは下の2点です。

  1. spec.description、spec.summaryの部分がTODOになっているので記述
  2. activesupportrspecの依存関係を追加

Gemfileにはgemspecと書いてあるのでノータッチ。

Gemをインストール

$ bundle install

テストを実装する

ディレクトリとファイルの準備

テストコードを置くディレクトリを作成する

$ mkdir spec

spec_helper.rbを作成する

# spec/spec_helper.rb

require 'rubygems'

# rspecの設定など
RSpec.configure do |config|
end

テストの実装

# spec/hello_zakihaya_spec.rb

require 'spec_helper'
require 'hello_zakihaya'

describe HelloZakihaya do
  describe "#say" do
    let(:hello_zakihaya) { HelloZakihaya.new }

    context "with modification" do
      it do
        expect(hello_zakihaya.say("small")).to eq "Hello small zakihaya"
      end 
    end 

    context "without modification" do
      it do
        expect(hello_zakihaya.say).to eq "Hello zakihaya"
      end 
    end 
  end 
end

テストを実行する時は

$ bundle exec rspec

Gemの中身を実装する

実行ファイルを編集

普通はこのファイルはmoduleにしておくそうなのですが、今回は1ファイルなのでclassにしちゃいました。

# lib/hello_zakihaya.rb

require "hello_zakihaya/version"
require "active_support/all"

class HelloZakihaya
  def say(modifiation = nil)
    if modifiation.present?
      "Hello #{modifiation} zakihaya"
    else
      "Hello zakihaya"
    end 
  end 
end

version.rbを編集

# lib/hello_zakihaya/version.rb 

class HelloZakihaya
  VERSION = "0.0.1"
end

テストを実行

$ bundle exec rspec
..

Finished in 0.00145 seconds (files took 0.31777 seconds to load)
2 examples, 0 failures

問題なさげ

試しに実行してみる

$ irb -I ./lib -rhello_zakihaya
irb(main):001:0> hello_zakihaya = HelloZakihaya.new
=> #<HelloZakihaya:0x007f871a271430>
irb(main):002:0> hello_zakihaya.say('small')
=> "Hello small zakihaya"
irb(main):003:0> hello_zakihaya.say
=> "Hello zakihaya"

何やらいい感じ

Gemfileを使って実行してみる

Gemfileの準備

適当なディレクトリを作成して、Gemfileを置く

# Gemfile

source 'https://rubygems.org'

gem "hello_zakihaya", git: 'git@github.com:zakihaya/hello-zakihaya.git'

Gemをインストール

$ bundle install --path=./gems
Fetching git@github.com:zakihaya/hello-zakihaya.git
Cloning into bare repository '/path_to_dir/zakihaya/gems/ruby/2.0.0/cache/bundler/git/hello-zakihaya-ab6b21fa940a70b867b4448a34a700d718504fad'...
remote: Counting objects: 32, done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 32 (delta 8), reused 29 (delta 5)
Receiving objects: 100% (32/32), 4.23 KiB | 0 bytes/s, done.
Resolving deltas: 100% (8/8), done.
Checking connectivity... done.
Cloning into '/path_to_dir/zakihaya/gems/ruby/2.0.0/bundler/gems/hello-zakihaya-23604ae1e4df'...
done.
Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Installing i18n (0.6.9) 
Installing json (1.8.1) 
Installing minitest (5.3.5) 
Installing thread_safe (0.3.4) 
Installing tzinfo (1.2.1) 
Installing activesupport (4.1.2) 
Using hello_zakihaya (0.0.1) from git@github.com:zakihaya/hello-zakihaya.git (at master) 
Using bundler (1.3.5) 
Your bundle is complete!
It was installed into ./gems

問題ナッシング

irbで実行

$ bundle exec irb
irb(main):001:0> require "hello_zakihaya"
=> true
irb(main):002:0> hello_zakihaya = HelloZakihaya.new
=> #<HelloZakihaya:0x007ff6120ca638>
irb(main):003:0> hello_zakihaya.say("small")
=> "Hello small zakihaya"
irb(main):004:0> hello_zakihaya.say
=> "Hello zakihaya"

Travis CIで自動テストを行う

Travis CIと連携する

TravisGithubのアカウントでログインし、連携をONにする

f:id:zakihaya:20140628154941p:plain

テスト結果をReadmeに表示

これを記述すればいいらしい

# README.md

[![Build Status](https://travis-ci.org/[YOUR_GITHUB_USERNAME]/[YOUR_PROJECT_NAME].png)](https://travis-ci.org/[YOUR_GITHUB_USERNAME]/[YOUR_PROJECT_NAME])

.travis.ymlファイルを追加

# .travis.yml

language: ruby
rvm:
  - 2.0.0

rakeタスクを追加

# Rakefile

require 'rspec/core/rake_task'
task :default => :spec
RSpec::Core::RakeTask.new

感想など

  • Gemは意外と簡単に作れる。(今回のはブログ書きながらで2時間半くらい)
  • Travis CIを初めて使ったのだが、これは便利。今後も積極的に使ってみたい。

ギモン

  • hello_zakihaya.rbで require "active_support/all" の部分を require "active_support/core_ext" にしてたら、Travisのテストが通らなかった。
    ローカルではOK。なぜだろう。。。

〜参考にさせていただきました〜

Ruby - Gemを作って、自動テストを回して、公開してみる方法 - Qiita

MySQL varcharのlengthはバイト数ではなく文字数

varchar(255)だったら、全角でも255文字まで入る。

よく忘れるのでメモ。


エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド

エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド

Macで使う言語を英語にしたらgit logがおかしくなった

英語を勉強したいなーと思ってMacのシステム言語をEnglishに変更しました。

そうしたらgit logが<A0>みたいな文字列になってしまったので、その対処法。


〜参考にさせて頂きました
How to fix locale issues in Mac OS X Lion terminal (Mountain Lion has the same issue) | Development as a lifestyle


どうもターミナルの文字コードの設定を変えればいいらしい。

.zshrcにこれを追記したら直りましたー。

export LC_CTYPE=en_US.UTF-8
export LC_ALL=en_US.UTF-8