Araoの技術ブログ

見習いエンジニアのAraoが、学んだことなどを書いたりするブログです。

権限がなくてGitHubにpushできなかった話

GitHubにpushしようとしたら、パーミッションに関するエラーメッセージが表示されてpushできなかった話です。
調べてみても解決方法がよく分からなかったので、数時間前の僕と同じエラーで困っている人のために書いてみます。
gitやGitHubに関しては初心者もいいところなので、書き方が変だったり言葉を誤って使用していたりするかもしれません。ご了承ください。

数週間前に、SSH公開鍵のGitHubへの登録を済ませ、Macのターミナルからお手軽にGitHubを使えるようになりました。その後、いろいろありまして、所属しているOrganizationのRepositoryに参加することになりました。
さっそくgit cloneして、コードをちょちょいと書き換え、addしてcommitしてpushしようとしたのですが、どうもpushができない。途中まで順調だったのに、なぜ?
というわけでエラーメッセージを読んでみます。

$ git push origin [branch name]
ERROR: Permission to [organization name]/[repository name].git denied to [user name].
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

何かとよく見かけるPermission deniedという文字列が観測されました。
きっと同じような理由ででつまずいている人がいるだろうという軽い気持ちで解決方法を調べてみたのですが、SSH周辺のトラブルに関する解決方法ばかりがヒットします。それも、pushでつまずいているのではなく、cloneのときにPermission deniedと言われているケースばかりです。先に書いた通り、git cloneは問題なく行えております。
念のため、SSH接続がちゃんとできているのか、確認してみました。

$ ssh -T git@github.com
Hi [user name]! You've successfully authenticated, but GitHub does not provide shell access.

バッチリですね。やはりSSH周辺のトラブルではないようです。
さて、解決方法が見つからない以上、自分で解決方法を模索する必要がでてきました。再度エラーメッセージを読んでみます。英語は不得手なので大雑把な理解になってしまいますが、「あなたが正しいアクセス権限を持っているか、リポジトリが存在しているか、確認してみてください」と言われているようです。
リポジトリはちゃんと存在しているので、あやしいのはアクセス権限です。pullはできるのにpushができないということは、Read onlyのような権限になっているのかもしれません。そこで、organizationに招待してくれた偉い人に確認してみることにしました。

僕「GitHubにpushができなくて、どうもアクセス権限周りがあやしいです。Read onlyの権限になっていないか、確認してみていただけますか」
偉い人「あれっ、ちょっと確認してみるね」

しばらくしたら、僕宛にGitHubから招待メールが届き、それを承諾してから再度GitHubから受け取ったメールには、You can now push to this repositoryなどと書かれておりました。
偉い人が何をどうしたのかはわかりませんが、これでpushができるようになったはずです。試してみましょう。

$ git push origin [branch name]
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 601 bytes | 0 bytes/s, done.
Total 4 (delta 3), reused 0 (delta 0)
(略)

できました。先ほどまで出ていたエラーメッセージが嘘のようです。権限が変更されたので当たり前ですが。

以上、GitHubにpushできなかった話でした。
あまり起こりそうにないトラブルでしたが、もし同じエラーメッセージが出て苦しんでいる方がいましたら、参考にしてみてください。そして管理者さんに確認をお願いしてみてください。

Rubyの研修その3 - Rubyっぽい書き方に挑戦してみた

Ruby勉強中の僕が、Rubyっぽい書き方に挑戦してみた話です。
この記事で使っているRubyのバージョンは2.1.4です。

この記事は前回の記事の続きになります。取り組んだ課題の内容も似ていますし、やっていることも前回の記事の終盤と同じです。
今回与えられた課題はこのようなものでした。

偉い人「タブ区切りテキスト、meibo.txtがあります。名前、性別、年齢の順に並んでいます。meibo.txtを年齢順に並び替えて出力してください」

meibo.txt

john    m   18
paul    m   20
alice   f   15
dabid   m   17
jasmin  f   17

meibo.txtは前回の課題と同じです。これを、年齢でソートして出力してあげればいいわけです。
この課題に対して、僕が最初に書いたコードはこんなコードでした。

sort1.rb

data = []
STDIN.each do |line|
    data.push(line.split)
end

data.sort_by! do |line|
    line[2]
end

for line in data do
    for num in 0..(line.length - 1) do
        if num == line.length - 1 then
            print line[num]
        else
            print line[num], "\t"
        end
    end
    print "\n"
end

うーん、今見直すと最高に鈍臭いですね。後半の二重のforループのところが特にひどいです。今になって言ってみると、なんだかC言語っぽい書き方です。
一応何をやっているのか解説します。まず、空の配列dataに、テキスト一行を空白文字で分割して得られた配列を順次プッシュします。次に、配列dataを年齢でソートします。最後に、ループを回して配列dataの中身を、適宜タブや改行を入れながら出力します。
これを提出したところ、返ってきた答えはこのようなものでした。

偉い人「出力するときにループを回すんじゃなくて、joinを使ってもっとスタイリッシュにしてみて」

どうやら出力するときにjoinを使ってあげると、ループを回す必要がなくなり、スタイリッシュになるようです。さっそくjoinについて調べてみます。

参考:Rubyリファレンス join (Array)

ふむふむ。配列を指定した区切り文字で結合した文字列を返すメソッドのようです。前回初めて使って、今回も使ったsplitとは、逆の操作をしていると考えればいいのでしょうか。
joinについてわかったところで、さっそく先ほどのsort1.rbをスタイリッシュに書き直してみました。

sort2.rb

data = []
STDIN.each{|line|
    data.push(line.split)
}

data.sort_by!{|line|
    line[2]
}

data.each{|line|
    puts line.join("\t")
}

変わったのは出力する部分だけですが、大幅にスタイリッシュになりました。さっきは出力する部分を10行も書いていましたが、今回のコードでは3行になっています。
気分よくこれを提出したところ、ちょっと意外な答えが返ってきました。

偉い人「dataを使わなくても書けるんじゃないかな。グローバルな変数を使わないで書けるはず」

なんと、dataを使わなくても書けるそうです。ここから先は偉い人のアドバイスを適宜受けながら進めていきました。
そして完成したコードがこちらです。

sort3.rb

puts STDIN.map{|line|
    line.split
}.sort_by{|line|
    line[2]
}.map{|line|
    line.join("\t")
}.join("\n")

前回の記事の最後みたいなコードになりました。いきなりputsから始まっているところや、mapが返した配列についてsort_by(前回はinject)しているところが、前回と共通しています。
こうやって書くと、スコープの広い変数を使っていないので、変数の初期化忘れや、どこかで変数が汚される心配がなく、バグが発生しにくいというメリットがあるようです。lineという名前の変数が3回登場しますが、これらのスコープは全てそのブロックの終わりまでなので、その3つのlineは全て異なるlineです。

こういう書き方をするかしないかは好みの問題だそうですが、せっかくRubyを書いているので、こういう書き方にもどんどん慣れ親しんでいきたいと思います。

Rubyの研修その2 - mapとinjectを使ってみた

Ruby勉強中の僕が、mapとinjectを使ってみたり、Rubyっぽい書き方に挑戦してみた話です。
この記事で使っているRubyのバージョンは2.1.4です。

例によって、研修の課題を受け取りました。

偉い人「タブ区切りテキスト、meibo.txtがあります。名前、性別、年齢の順に並んでいます。年齢の平均を標準出力に出力してください」

meibo.txt

john    m   18
paul    m   20
alice   f   15
dabid   m   17
jasmin  f   17

meibo.txtを見た僕「(Rubyでのファイルオープンの方法は知らないから調べるとして、二次元配列のようなものに格納してあげれば、あとはどうにでもできるはずだ)」
というわけで、ファイルオープンやgets、splitについて調べ、最初にこんなコードを書きました。

avg1.rb

f = open("meibo.txt")
index = 0
data = []
while line = f.gets do
    data[index] = line.split
    index += 1
end
f.close

sum = 0
data.each do |person|
     sum += person[2].to_f
end
avg = (sum / data.length).round(2)
puts avg

まず、ファイルをオープンし、一行ずつ読み込んだものを空白文字で分割して得られた配列を、配列dataの要素として順に代入します。次に、配列dataの各要素(配列)の年齢の部分を順次足していって、年齢の合計を求めます。最後に、年齢の合計を配列dataの要素数(人数)で割って、年齢の平均を求めて四捨五入で少数第二位までに丸め、標準出力に出力します。
我ながらよく書けているんじゃないかと意気揚々と偉い人に提出したのですが、返ってきた答えは(そのときの僕にとっては)意外なものでした。

偉い人「じゃあRubyっぽくmapとかinjectとか使ってみようか。これはこれでお行儀よく書けているけど」

Rubyにはmapやinjectというメソッドがあって、これらを使えばもっとRubyっぽく、シンプルに(?)書けるということなのでしょうか。そんなわけで、mapとinjectについて調べてみます。

参考:Rubyリファレンス map, map! (Array)Rubyリファレンス inject (Enumerable)

なるほど。mapは配列の各要素に関してブロックを実行し、そのブロックの戻り値を集めた配列を返すメソッドで、injectは繰り返し計算に計算に用いるメソッドということらしいです。
injectの方は最初ちょっとよくわからなかったんですが、計算を始める前に初期値が0のsumのような変数を用意する必要がない(injectしてあげるといきなり合計値がババーンと得られる)と理解しました。

mapとinjectがどんなものかわかったところで、さっそく先ほど書いたavg1.rbを書き直してみました。

avg2.rb

ages = STDIN.map do |line|
    line.split[2].to_f
end

sum = ages.inject do |sum, num|
    sum + num
end

puts (sum / ages.length).round(2)

さっきと比べてだいぶすっきりしました。先ほどのdataのような配列はなく、変数sumもinjectの戻り値をデデーンと代入しているだけです。
mapは配列だけでなく、複数行の標準入力に対しても同じようなことができるみたいですね。
ちなみに、さっきと違って標準入力からmeibo.txtを読み込んでいるので、実行するときはこのようにしてあげる必要があります。

$ ruby avg2.rb < meibo.txt

さて、ここから先は少し余談になってしまうのですが、偉い人からのコメントを受け、よりRubyっぽい書き方にも挑戦してみました。
もし今回の問題が、年齢の平均を求めるのではなく、年齢の合計を求めるものだったらどのように書けるでしょうか。

現時点の僕がめいっぱいRubyっぽくしてみると、こんな感じになります。

sum.rb

puts STDIN.map{|line|
    line.split[2].to_i
}.inject{|sum, num|
    sum + num
}

ぱっと見なんのことだかわからないかと思いますが、やっていることは先ほどとほとんど同じです。ただし今回は、人数(要素数)で割るという操作が必要ないため、こんなにシンプルになりました。
また、先ほどまではeachやmap、injectを使う際にdoとendで書いていたのを、代わりに"{ }"で書いています。こうすることで、mapが返した配列についてさらにinjectしてあげる、ということが可能になっています。

こういうのを書けるようになると、Rubyに慣れてきたなと実感しますね。