本家の最新を取り込みながら安全に自分のリポジトリで修正したいとき
はじめに
本家リポジトリをforkして、それを上流に設定し、最新を取り込みながらfork先のリポジトリに修正したいときに今回の手順を踏みます。
本家に直接pushができないときに、こういった形を取ることがあります。
この流れを"Fork & Pull モデル"と呼びます。
本家リポジトリをfork
取り込みたいリポジトリを、自身のアカウントなりでforkします。
GitHubからでもいいですし、hubコマンドがある方は"hub fork"でもいいです。
hubコマンドとは、CLIでGitHubの操作ができるツールです。
github.com
リモート一覧にupstream追加
取り込みたいリポジトリを"upstream"としてリモート一覧に追加します。
% git remote add upstream git@github.com:[organization or user]/[repository].git
間違って本家からリポジトリを取ってきてしまったときは、originのリモートURLを変更すればよいです。
% git remote set-url origin git@github.com:kitakitabauer/[repository].git
リモート一覧を確認すると、"upstream" が追加されていることがわかります。
% git remote -v origin git@github.com:kitakitabauer/[repository].git (fetch) origin git@github.com:kitakitabauer/[repository].git (push) upstream git@github.com:[organization or user]/[repository].git (fetch) upstream git@github.com:[organization or user]/[repository].git (push)
上流の更新を取り込む
上流の更新を取得して反映させる場合は下記の通りです。
% git fetch upstream % git merge upstream/master
または一度にpullしてしまいます。
% git pull upstream master
ブランチ作成
ローカルリポジトリにブランチを作成してcheckoutします。
% git checkout -b [開発ブランチ名] Switched to a new branch '開発ブランチ名'
新しいブランチに切り替わっていることを確認します。
% git branch * [開発ブランチ名] master
originにpush
originに対して、ローカルで作成したブランチ名を指定して、空ブランチをpushします。
% git push origin [開発ブランチ名] Total 0 (delta 0), reused 0 (delta 0) To git@github.com:kitakitabauer/[開発repository].git * [new branch] 開発ブランチ名 -> 開発ブランチ名
CI の結果を確認する場合
% hub ci-status
上流のmasterブランチへPR作成
- iでissueに紐付けて作成できます。
hub pull-request [branch name] -i [issue id]
おしまい。
Goのことはじめ その3:IntelliJ IDEAでGoを書くために
はじめに
これまでNode.jsやPythonではVimを使ってきましたが、WebStormなど優秀なIDEが登場してきたこともあり、Goをお仕事で書くタイミングでそろそろIDEにも慣れておきたく、IntelliJ IDEAを使うことにしました。
IntelliJ IDEA(インテリジェイ アイディア)とは
チェコに本社を置くJetBrains社が開発した、Java言語など多言語対応の統合開発環境です。
EclipseやNetBeans の競合に当たりますが、Javaに関していえば、周りのエンジニアはこちらを使っている人が多いです。
Community(無償版) or Ultimate(有償版)
Golangはデフォルトではサポートされていませんが、IntelliJのための下記プラグインを使うことでGo言語に対応させることができます。
github.com
Goを書くだけなら事足りるので、迷わず無償版を選択します。もちろんライセンスを購入できるなら有償版でもかまわないかと思います。
詳しい違いはこちらにあります。
インストール
下記からダウンロードして展開してもいいですが、今回はbrew-caskでインストールします。
www.jetbrains.com
もしbrew caskがない場合は下記コマンドで準備します。
% brew tap phinze/homebrew-cask % brew install brew-cask % brew tap caskroom/versions
brew caskの準備が整ったら"intellij"で検索します。
% brew cask search intellij
==> Partial matches
caskroom/cask/intellij-idea intellij-idea-ce intellij-idea-ce-eap intellij-idea-eap intellij-idea-next-ce-eap intellij-idea-next-eap caskroom/cask/intellij-idea
"intellij-idea-ce"がCommunity Edition(無償版)なので、こちらの詳細を確認し、インストールします。ほかは有償版やRC版です。
% brew cask info intellij-idea-ce intellij-idea-ce: 2016.2.4 https://www.jetbrains.com/idea/ /usr/local/Caskroom/intellij-idea-ce/2016.2.4 (68B) From: https://github.com/caskroom/homebrew-versions/blob/master/Casks/intellij-idea-ce.rb ==> Names IntelliJ IDEA Community Edition IntelliJ IDEA CE ==> Artifacts IntelliJ IDEA CE.app (app) % brew cask install intellij-idea-ce
IntelliJ Goプラグインダウンロード・適用
IntelliJ IDEAを起動して、Configure→Preferencesをクリックします。
Plugins→"Search in repositories"リンクをクリックします。
検索窓に"Go"を入力して、先程のプラグインをインストールします。
その後intelliJの再起動を促されるので、再起動します。
GitHubのプロジェクトをインポート
ここでは、GitHubのgoプロジェクトをインポートする手順をまとめます。
再び起動したIntelliJにて、configure→Version Control→GitHubから、アカウントを入力し、テスト認証してみます。
問題なければ"OK"でトップに戻った後、"Check out from Version Control"→GitHubをクリックします。
インポートしたいGitHubのリポジトリURLを入力します。
"Parent Directory"は、ローカルのGOPATHが通っているパスを指定します。
Clone後、再びトップに戻って、"Create New Project"を選択します。
その後、"Create project from existing sources"をクリックします。
Project Name/Project locationはそのままとします。
ISUCON6で屈辱的に負けてきた
はじめに
去る2016年9月18日(日)に、ISUCON6の予選に参加しました。
結果は最高スコア12023で屈辱的に敗退しました・・・
その一部始終をまとめておきたいと思います。
予選当日までにやってきたこと
主に下記のことを行ってきました。
1. チームメンバーとISUCON4(2014年開催)の過去問で復習をしてきました。
kitakitabauer.hatenablog.com
kitakitabauer.hatenablog.com
kitakitabauer.hatenablog.com
kitakitabauer.hatenablog.com
2. 今回予選で利用するサーバはMicrosoft Azureなので、アカウントを用意した後、リーダーがリソースグループ作成と、サブスクリプションへのメンバーID登録をしてくれました。
3. bitbucketに作成された共有リポジトリに、作業端末の公開鍵を入れたauthorized_keysなどをコミットしていました。作成されたインスタンスにこれらを突っ込む予定です。
4. ローカルにNode, Goの最新バージョンをインストールしました。言語選定の理由は後述します。
5. アンチョコとして、my.cnfとnginx.confを用意しておきました。
6. 予選会場は自社の会議室を間借りさせてもらうことになったので、その手配と人数分のモニターの確保をしてもらいました。
チームメンバー
Aragamiというチーム名で参加しました。(なぜか自社の障害チケット管理システムの名前を拝借)
構成メンバーは、自社でカジュアルソーシャルコーディングという、カジュアルな勉強会を4年以上続けてきたチームメンバーで参加しました。
- id:nakimura:チームリーダー。主夫。アプリ担当。
- @wataru420:HipHopper。ミドルウェア周りのチューニング担当。
- id:kitakitabauer:私。モグリエンジニア。アプリ担当。
という作業割り振りで行こうと決まりました。
見づらくなるので、誰が何をやったとかは基本的に省略して書いていきます。
言語選定ですが、
高速化対象のソフトウェアとして主催者から Perl, Ruby, Python, PHP, Node.js, Go, Scala によるWebアプリケーションが与えられる。 ただし各々の性能が一致することを主催者は保証しない。どれをベースに用いてもよいし、独自で実装したものを用いてもよい。
ということで、メンバー中2人が Node.js での実務経験が豊富だったことから、
Node > Go >>>>>>>>> Python >>>>>>>>>>>>>>>>> PHP
の順で、当日に実装内容を見てから決めようということになりました。
*1
いきなりはまった
10:00
予選が開始し、運営から事前に共有されていたポータルサイトが参照・ログイン可能となりました。
と同時に予選用イメージURLが閲覧可能となったので、Azureのリソースグループにインスタンス起動・公開鍵登録後、そこに表示されたPublic IP addressに対してSSHログインしようとしたら…
「SSHログインできない・・・!」
もう出だしから一人つまづいて死にたいと思っていたけど、死ぬのをやめて生きて原因を探しました。
こんな感じの~/.ssh/configで、自分はいつもユーザ名を指定せずに"ssh XX.XX.XX.XXX"していたのですが、
user kitakitabauer … Host XX.XX.XX.XXX User isucon IdentityFile ~/.ssh/id_rsa_github
userをHost毎に上書きしてくれている(つまり、Host XX.XX.XX.XXXではisuconユーザでsshしてくれる)と勘違いしていたため、ログインできなかったという恥ずかしいオチでまた死にたくなりました。
生きてユーザ指定して、無事sshできました。
Webアプリの構成を確認
11:00
ベンチマークサイトは、簡易的な辞書ページのようなものでした。
ベンチマークはキューイング方式で、それぞれのチームがベンチマークのキューに登録して実行を待つという感じです。
Node.jsの構成はざっくりこんな感じでした。
./js ├── bin │ ├── isuda # isudaプロセス起動 │ ├── isutar # isutarプロセス起動 │ … │ └── www ├── route │ ├── isuda.js # ルーティングされたリクエストパスの処理詳細/isudaデータベースへのセットアップなど │ └── isutar.js # ルーティングされたリクエストパスの処理詳細/isutarデータベースへのセットアップなど ├── views # テンプレートエンジンによる画面描画 │ … │ └── index.ejs ├── .gitignore ├── isuda.js # isudaプロセスのセットアップ/リクエストのルーティング等 ├── isutar.js # isutarプロセスのセットアップ/リクエストのルーティング等 ├── npm-shrinkwrap.json └── package.json
Node実装の確認
Nodeの実装を確認してびっくり!過去問から見る例年のものと比べて想像以上にしっかり実装されていました。
Webフレームワークはkoa。
しかもES7のAsync Awaitで書かれている。これはまずい。koaやAsync Awaitは勉強不足だ…。
それでもGoよりかはましということで、systemctrlで、実行言語をNodeに変更しました。
ベンチマーク初回実行
とりあえずベンチマークが動くことを確認したかったので、キューに入れていざ実行したところ、0点。
スコア計算ですが、エラーよりも、レスポンス遅延のほうが大きく減点されるようで、
実行後のメッセージ欄にはそれほどFailが確認できなかったことから、レスポンス遅延による減点が大きいのかもと推測。
マシンリソースやアプリ以外のボトルネックを確認
その後、アプリをじっくり確認する前に行ったことです。
- 現時点のwebappをバックアップとしてbitbucketにpush
- restart.shを作って、slackにWebhook設定することで、restartを1オペでかつslack上からも再起動を確認できるように
- CPUコアは2コア(これはAzureポータルからも確認できた)やメモリは7GBで潤沢なことを確認
- 余計なミドルウェアやツールが起動してリソースを逼迫していないか確認
- ベンチマークを流したあと、データ登録後のMySQLの容量が肥大化しすぎていないか確認
- アンチョコのmy.cnfを置いて、MySQLのslowqueryをONにしてスロークエリを確認
特段おかしなところはなさそう。
これはまさか、今回アプリをどれだけ改修できるかが勝負となる・・?
kataribeでボトルネック調査
13:00
kataribeを入れて時間がかかっているHTTPリクエストをサマリすることでボトルネックを確認したところ、静的ファイルのリクエストに時間がかかっていると出ていたので、nginxでキャッシングして返す・クライアントでもキャッシュできるならするようにしました。
すると、"/"、"/keyword"、"/login"へのアクセスが異様に遅いことが明白になっていきました。
君の名は。
いろんなリクエストから呼ばれている処理の中で、ユーザの名前を毎回DBから取得していたので、メモリにマップを持たせて初回のみ取得するように。
const setName = async (ctx) => { ctx.state = {}; const db = await dbh(ctx); const userId = ctx.session.userId; if (userId != null) { - const users = await db.query('SELECT name FROM user WHERE id = ?', [userId.toString()]); - if (users.length > 0) { - ctx.state.user_name = users[0].name; + let name = userNameMap[userId]; + if (name === undefined) { + const users = await db.query('SELECT name FROM user WHERE id = ?', [userId.toString()]); + name = users[0] && users[0].name; + } + if (name) { + userNameMap[userId] = name; + ctx.state.user_name = name;
スコアは0。これぐらいじゃあまだまだ。
Nodeで一つ一つのIO処理待ってる意味 is 何?
最も遅い"/"へのリクエストの中で、for文内でAsync Awaitで1つ1つ直列に処理している部分がひどいので、Promiseに詰めて並列実行できるように(リーダーが)改善しました。
- for (let entry of entries) { Add a comment to this line - entry.html = await htmlify(ctx, entry.description); - entry.stars = await loadStars(ctx, entry.keyword); - } + + const tasks = entries.map(entry => { + return Promise.all([ + htmlify(ctx, entry.description), + loadStars(ctx, entry.keyword), + ]).then(result => { + entry.html = result[0]; + entry.stars = result[1]; + }); + }); + await Promise.all(tasks);
スコアはまだ0のままうんともすんとも。
キーワード長を毎回取得するなんて
MySQLクエリ解析にかけるほどでもなく明らかだったのですが、下記は"keyword"を長さ順に全件取得しているので重いです。
SELECT * FROM entry ORDER BY CHARACTER_LENGTH(keyword) DESC
キーワード長は不変なので、キーワードをDBに登録する処理の時に、長さも合わせて保存することで、HTMLページのキーワードリンク生成処理時に毎回レングスを取ってこなくてもいいように変更。
ALTER TABLE entry ADD COLUMN `keyword_length` int(11) after `keyword`
await db.query( - 'INSERT INTO entry (author_id, keyword, description, created_at, updated_at) ' + - 'VALUES (?, ?, ?, NOW(), NOW()) ' + + 'INSERT INTO entry (author_id, keyword, keyword_length, description, created_at, updated_at) ' + + 'VALUES (?, ?, ?, CHARACTER_LENGTH(?), NOW(), NOW()) ' + 'ON DUPLICATE KEY UPDATE ' + - 'author_id = ?, keyword = ?, description = ?, updated_at = NOW()', + 'author_id = ?, keyword = ?, keyword_length = CHARACTER_LENGTH(?), description = ?, updated_at = NOW()', [ - userId, keyword, description, userId, keyword, description + userId, keyword, keyword, description, userId, keyword, keyword, description ]);
うーん、いまだスコアは0のまま。
initializeでイニシアチブとってこ
HTMLページのキーワードリンクを生成するための処理の中で、for文で全ての登録キーワードをDBから毎回取得して正規表現でゴニョゴニョしている部分を、まずinitializeで一度行うように(これもリーダーが)変更して、その後新しいキーワードが登録されたときだけ、その正規表現を更新するように。
あと、entryテーブルの全フィールドをSELECTしていたのを"keyword"だけにするのも同時に。
これはかなりききそうだけど、まだスコアは0のまま。。ほんとに上がるのか・・?
(後々、他の箇所の実装で書かれたSQL構文が間違っていたことでスコアが上がらなかったことに気づきました。なので、ここだけの伸びはわかりかねますが、多分今回対応できた中ではここが一番効いたのかと!)
パスワード=名前説
14:00
/login へのリクエストのときに、SHA-1でメッセージダイジェストを生成して、userテーブルのパスワードと比較していますが、この生成されたメッセージダイジェストを保存するようにしたら、同じユーザのログインが高速化されるかもという話が上がり、改修していきました。
すると、そもそもリクエストされたパスワードがユーザのnameと一緒だという衝撃の事実が明らかになりました。
これは、ちゃんとHTTPステータス4xxのものは弾いて、それ以外はすんなりログインさせれば、かなりの高速化が見込めそうだということで修正した結果、ようやくスコアが 0 → 約6000 まで上がりました。
こんなマイクロサービスは嫌だ
15:00
コードをじっくり読んでいくと、isudaとisutarが、互いにHTTPアクセスして、アクセス先のプロセスでisuda/isutarデータベース要求している部分が完全に無駄なので、それぞれの実装にDBアクセス設定をゴリゴリ書いてアクセスするようにしました。
これによって 約6000 → 9970 まで上がりました!
もっと余裕があれば、isudaとisutarの完全統合までやりたかった。
Nodeプロセス数が微妙に多い、そう微妙に。
17:00
起動するisudaとisutarのNodeプロセス数を、CPUコア数に合わせて3→2ずつに変更
9970 → これまでのベストスコア 12023 に!
と、ここまででタイムリミット。最後の最後に1万超えは嬉しかった…
ISUCONに参加してみて
個人的な反省点は下記の通り。
- 最後までサーバローカルで直接ソースコードを編集していたので、同時に修正できなかった。
- 結構思い込みでコーディングしてた中でミスも多々あったので、レビューもし易いことを考えても効率は上がるはず
- 声を掛け合っていたので、デグレードが起きなかったのはせめてもの救い
- 次はチェックリストを作っておいて、作業の経過が見えたり、よりバッティングしないようにしたほうがいいかも
- id:foostanさんのように、GitHubのProjectsを使ってみるのもいいなぁと
- Async Awaitとかkoaとか、もっとEcmaScriptやNodeの新しめの仕様を勉強しておけばよかった
- ページングのために、キーワードの総数を毎回SELECT COUNTしていたけど、キーワード登録のときにメモリに回数を持って、それを使うようにすればそこそこ速くなったはず。途中まで実装していたけど間に合わず。
- html生成の部分がもっとも重いことは明白だったので、トライ木の構築をしてキャッシュすればかなり速くなるということで、id:nakimuraさんが最後の最後までライブラリを検証しながらテストしてたけど結局スコアが落ちてしまい、導入を断念orz
- トライ木の構築はメモリは食うのだけど、だからこそメモリが潤沢だったと思われるだけに残念…
ISUCON、前々から興味はあったけど、自分の現在地を知るのが恐くて参加できずにいました。
そしてこんな優秀なメンバーと参加できた以上、もっと貢献できたらなぁと思う場面ばかりで、悔やまれるばかりです。
でもめっちゃ楽しかった!プライベートな時間は結構費やしたけど、とても勉強になったので、もっと周りのISUCON人口を増やして切磋琢磨したいです。
本選に出場されたチームの皆様、当日は激しい闘いを期待しています!
最後に、ISUCON運営の皆様、本当にありがとうございました!
まだ本選は控えていますが、来年も激しく楽しい大会の開催を期待しています!
*1:ちなみに、C++で独自実装したチームもいたそうです。しかも予選通過。すごすぎる…!
ghq/peco/hubを組み合わせてローカル・リモートリポジトリに即座にたどり着く
はじめに
あまりにも有名な下記ツールを組み合わせて、ストレスなく、リポジトリにたどり着くような設定にしたのでまとめておきます。
あまりにも有名なので、ツールの説明は割愛します。が、ざっくりいうと、
Go製のリポジトリ管理ツール
github.com
同じくGo製の使い勝手のいいフィルタリングツール
github.com
GitHubをCLIで操作するツール
github.com
ですw
ローカル編
ローカルにcloneしたリポジトリを一覧して選択したリポジトリに移動する 操作を素早く行えるようにしています。
zshだとこんな感じの設定になります。
function peco-src () { local selected_dir=$(ghq list -p | peco --query "$LBUFFER") if [ -n "$selected_dir" ]; then BUFFER="cd ${selected_dir}" # pecoで選択中, Enter を押した瞬間に実行する zle accept-line fi zle clear-screen } # 関数をウィジェットに登録 zle -N peco-src bindkey '^]' peco-src
"ghq list -p" で、"ghq get"でローカルにクローンしたリポジトリ一覧を表示します。
パイプでつないだ'peco --query "$LBUFFER"'で、その一覧をpecoのコンソールに表示させます。
このようなやり方は、他のQiitaの記事でも紹介されている(というかほぼパクリ)ので、目新しいことはありません。
リモート編
リモートリポジトリを一覧し、選択したリポジトリをブラウザで表示します。
下記は、先程のローカル用の関数をカスタマイズして作成した関数です。
function peco-src-remote () { local selected_repo=$(ghq list -p | peco --query "$LBUFFER" | rev | cut -d "/" -f -2 | rev) echo $selected_repo if [ -n "$selected_repo" ]; then BUFFER="hub browse ${selected_repo}" zle accept-line fi zle clear-screen } zle -N peco-src-remote bindkey '^^' peco-src-remote
解説
hubツールに"browse"というコマンドを使って、リモートのリポジトリページをブラウザで開くことができます。
$ hub browse [ユーザ名/リポジトリ名]
なので、下記のようなローカルリポジトリのパス文字列を分解して"hub browse"に渡すということをしたいとおもいます。
$ /Users/kitakitabauer/.ghq/github.com/ユーザ名/リポジトリ名
しかし、$GOPATHやghqのルートによって、パス階層がどのぐらいになるのかわからないので、渡す文字列を固定で切り出すことがめんどくさそうです。
- /Users/kitakitabauer/.ghq/github.com/kitakitabauer/dotfiles
- /Users/kitakitabauer/go/src/github.com/kitakitabauer/dotfiles
- /Users/kitakitabauer/go/src/bitbucket.org/kitakitabauer/dotfiles
自分は以前の記事でご紹介しましたが、".ghq" や "go/src" のように、cloneするディレクトリを分けているので、単純に前者と後者で階層に1つ差が生まれます。
そこで、今回の記事のポイントなのですが、汎用的に"hub browse"に渡したい文字列を整形できるようにしました。
ポイントは下記の部分です。
rev | cut -d "/" -f -2 | rev
1. rev で文字列をひっくり返す
名リトジポリ/名ザーユ/moc.buhtig/qhg./reuabatikatik/sresU/
2. cut -d "/" -f -2 で、スラッシュ区切りで先頭から2フィールド目までを取り出す
名リトジポリ/名ザーユ
3. rev で再び逆にして、ユーザ名/リポジトリ名の順番に戻す
ユーザ名/リポジトリ名
最後に作成した関数を"bindkey"コマンドで、割当がないキーにあてることで一発呼び出しができます。
bindkey '^]' peco-src bindkey '^^' peco-src-remote
今回のzshの設定を含めたdotfilesはこちらにコミットしています。
github.com
rev は使い道に迷いますが、cut と組み合わせることで他にも面白いことができそうです。
おしまい。
Goのことはじめ その1:GOPATHとghqのルート指定
はじめに
業務でGoを触ることになったので、Goに関して気になったことをまとめていこうと思います。
GOPATH
GOPATHはGoで使う環境変数なのですが、2つの役割があります。
- ビルド時のインポートパスのルート(正確には$GOPATH/src配下)
- go getコマンドで外部パッケージをインストールしたときの、ダウンロードルートディレクトリ
詳細は公式ドキュメントに記載されています。
How to Write Go Code - The Go Programming Language
ghq
id:motemen さん作の、Go製のリポジトリ管理ツールです。
github.com
$ ghq get (GitHubの)ユーザ名/リポジトリ名
とすると、ローカルの "~/.ghq" というディレクトリにデフォルトでインストールされます。
"ユーザ名/リポジトリ名"の部分ですが、GitHub以外、例えばbitbucketは"https://ユーザ名@bitbucket.org/…"のように一から指定する必要があります。
Goにはgetコマンドがあるので、事足りるような気もするのですが、他にもクローンされたリポジトリを一覧したり、指定したローカルのリポジトリにcdできるので、重宝しています。
GOPATHとghqのルートをどこに設定するか
「GOPATHは適当に決めて問題ない」という記事もありましたが、個人的には適当に決めてあとで地雷を踏みたくないのが性分です。
例えば".ghq"の下にgetしたけど、やっぱりこのライブラリをインポートしたいから"go/src"の下に入れたい…となったときにシンボリックリンクを貼るのか、同じものをgo getするのかなど、色々困りそうです。
それを踏まえて、自分はこのようにしようと思います。
$GOPATH
export GOPATH=~/go
.gitconfig
[ghq] root = ~/.ghq root = ~/go/src
Go 言語のソースだけは go get で 取得し "$GOPATH/src" 以下に
そうでないものは ghq get で "~/.ghq" 以下に取得するようにしています。
今回はここまでです。
次回はGoのバージョン管理システム選定について書きたいと思います。
dotfiles整理 その2:zshrcから分割した設定ファイルが読み込まれない問題への対処
kitakitabauer.hatenablog.com
前回の記事はこちら。
この構成に変えたあと、.zprofileに記述した部分が読み込まれなくなりました。
ターミナルアプリで起動されるのは、ログインシェルではなく、インタラクティブシェル
という事実を下記記事にて知りました…!
qiita.com
自分はターミナルアプリにiTerm2を使っており、下記の通りbrewインストールしたzshを起動しています。
前回の記事で「zshが起動時に読み込むファイルとその順番」について記述しましたが、
ログインシェルでないと、$ZDOTDIR/.zprofile は読み込まれません。
かといって、"Send text as start: "に "source $HOME/.zprofile" を入れて毎回読み込むのも、インタラクティブシェルのときに毎回読み込むことになるので、zsh標準の仕様と外れるので気持ち悪いし、セッションを起動するたびにそのコマンドが毎回出力されるのも嫌です。
ログインシェル変更
ターミナルアプリ上ではなく、ローカル設定からzshを指定するようにして対応することとします。
(なので、iTerm上の設定は"Command"→"Login shell"に変更しておきます)
まずは現在のインストールシェル一覧を確認
$ cat /etc/shells # List of acceptable shells for chpass(1). # Ftpd will not allow users to connect who are not using # one of these shells. /bin/bash /bin/csh /bin/ksh /bin/sh /bin/tcsh /bin/zsh
/etc/shellsに、brewインストールされたzshのパスを追加
デフォルトインストールされたzshはバージョンが少し古いため、brewインストールしたzshのパスをroot権限で追加します。
$ sudo vi /etc/shells … /bin/zsh /usr/local/bin/zsh # 追加
ログインシェル変更
デフォルトのシェルを先ほど追加したzshに変更します。
下記コマンドによって、/etc/shellsの一番最後に記述されたzshに変更します。
$ chsh -s "$(command -v zsh)" "${USER}"
確認
読み込んだ順番を確認するために、それぞれの設定ファイルにechoを入れたあと、
ターミナルアプリの別セッションを立ち上げると、
~/.zshenv ~/.zsh/.zshenv ~/.zsh/.zprofile ~/.zsh/.zshrc
問題なし!
尚、ログインシェル変更の方法は色々あるらしいです。
rcmdnk.github.io
dotfiles整理 その1:zshの構成を見なおそう
はじめに
最近ちょっと時間ができたので、随分放置してきたdotfilesを整理をすることにしました。
まずはzsh。
これまでは.zshrcに全て記述していたので、前々から役割毎に分けたいと思っていました。
大まかな方針は下記の通りです。
構成はこんな感じを想定しています。
/Users/kitakitabauer/. ├── .zshenv -> /Users/kitakitabauer/dotfiles/.zshenv ├── .zsh │ ├──.zprofile -> /Users/kitakitabauer/dotfiles/.zsh/.zprofile │ ├──.zshenv -> /Users/kitakitabauer/dotfiles/.zsh/.zshenv │ └──.zshrc -> /Users/kitakitabauer/dotfiles/.zsh/.zshrc └── …
環境変数 ZDOTDIR の設定
ホームディレクトリがzsh関連のファイルで埋まるのが嫌なので、ZDOTDIR を指定して、zsh関連の設定ファイルを別フォルダにて管理します。
ZDOTDIR は$HOME/.zshenv に記述します。
export ZDOTDIR=$HOME/.zsh source $ZDOTDIR/.zshenv
これで、$HOMEに置く必要があるのは.zshenvだけになります。
zshが起動時に読み込むファイルとその順番
zshには様々な設定ファイルがあり、それらは決まった順番で読み込まれます。
ログインシェルは下記の順番で読み込まれます。
/etc/zshenv $ZDOTDIR/.zshenv /etc/zprofile $ZDOTDIR/.zprofile /etc/zshrc $ZDOTDIR/.zshrc /etc/zlogin $ZDOTDIR/.zlogin
インタラクティブシェルは下記の順番で読み込まれます。
/etc/zshenv $ZDOTDIR/.zshenv /etc/zshrc $ZDOTDIR/.zshrc
ログインシェルとしてzshを使うので、前述の通り.zshenv、.zprofile、.zshrcの構成でいきます。
.zloginは今のところ使用しないので作成しません。
尚、全てのユーザに影響するため、etc配下の設定ファイルは変更しません。
それぞれの設定ファイルに何を記述するか
$ZDOTDIR/.zshenv
環境変数系。ヒストリー系もここで記述します。
$ZDOTDIR/.zprofile
ログインシェルだけで使いたいaliasを記述します。
$ZDOTDIR/.zshrc
上述以外の全てを記述します。
dotfilesのシンボリックリンク生成
最後にsymlink.shにて、gitリポジトリのdotfilesに対して、$HOMEにシンボリックリンクを貼ります。
basepath=$(cd $(dirname $0);pwd) # symlink dotfiles into ~files=.* for file in $files do if [ ! -d $file -a $file != "." -a $file != ".." -a $file != ".git" ] ; then ln -sf $basepath/$file ~ fi done # symlink zsh configuration files into ~/.zsh if [ ! -d ~/.zsh ] ; then mkdir ~/.zsh fi for file in .zsh/.* do if [ $file != "." -a $file != ".." -a $file != ".git" ] ; then ln -sf $basepath/$file ~/.zsh/ fi done
完成
まだ工事中ですが、一旦こんな感じになりました。
github.com
次の記事はこちら
kitakitabauer.hatenablog.com