$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

「エンジニアリング組織論への招待」はいろんな立場の人に読んで欲しい

最近メンタリング制度のことや、技術組織のことについて興味がある。最近「エンジニアリング組織論への招待」という本が出版されて話題になっていたので読んでみた。

この本は、エンジニアリングで重要なのは「どうしたら効率よく不確実性を減らしていけるのか」ということと述べている。その考え方に従って、思考方法、メンタリング、チーム運営、組織運営といったプログラミング以外でのやるべきことについて、様々な背景も含めて教えてくれる。

全部読んでみたところ本当に良い本であった。メンタリングや組織運営といった、なかなか汎用化や言語化がしにくい分野を、納得のできる形で言語化されていて本当にすごい。僕は最近はメンタリング制度について考えているので、特にChapter2のメンタリングの技術の章が一番おもしろかった。

この本は、メンターをやっている・マネージャーををやっている・チームのエンジニアリーダーをやっている・技術組織に興味があるなどの人は是非一読すると良いと思う。全部読むのはページ数も多くてきついという人は、Chapter1の思考のリファクタリングは読んだ上で、メンターならChapter2のメンタリングの技術を、チームのリーダーならChapter3のアジャイルなチームの原理やChapter4の学習するチームと不確実性マネジメントを、技術組織の運営に興味があるならChapter5の技術組織の力学とアーキテクチャを、とかいつまんで読むだけでも参考になると思う。

僕がこの本の中で印象に残ったのは

  • コントロールできないものをコントロールしようとしない
  • メンタリングとは何をするものなのか

という二点。

コントロールできないものをコントロールしようとしない

この本に以下のようなことが書いてあった。

  • 日常的に発生する様々な問題に思いを悩ませているとき、「コントロールできないものをコントロールしようとしている」ため、いつまでたっても、問題解決の糸口が見つからないということがある
    • たとえば「自分の上司が自分の仕事をあまり評価してくれない」とずっと愚痴を言っているけど、特にアクションしていない場合、大抵「あいつの評価基準が悪いからだ」などとコントロールできないものについて考えてしまっている
  • 問題を解決したいなら、コントロールできるもの・できないものを冷静に判断する必要がある

たとえば「自分の上司が自分の仕事をあまり評価してくれない」という場合、コントロールできないものは

  • 上司の自分に対する内心での評価
  • 上司の評価基準

などで、対してコントロールできるのは

  • 上司の評価基準を詳しく聞くという行動
  • 上司の評価基準に合わせた自身の行動の変化
  • 上司を変えるための異動などの行動
  • 自分の仕事を上司に詳しく説明するという行動
  • 自分自身の思いを上司に知ってもらうための行動

などである。そこで問題解決するには、コントロールできない「上司の自分に対する内心での評価」「上司の評価基準」は考えず、コントロールできる行動について考え、自分から実行していくことが重要ということであった。


これを読んで、確かに、あの人が悪い・会社が悪い・この制度はおかしいなどと、今困っていることの責任を自分以外に押し付け、自分は何もしないということはやってしまいがちだなと感じた。今困っていることの責任を、自分のコントロールできないものに押し付けているように見える。こういうときは絶対に問題は解決しない。

今後はそのように他責な考えになっているときはスパッと頭を切り替え、「では自分がその問題に対して行動を起こせるものはないか」という風にすぐに考えるようにしたいなと思う。

メンタリングとは何をするものなのか

今の自分の興味がメンタリングにあったこともあって、この本のChapter2のメンタリングの技術の章は本当に面白かった。その中で色んな場所で「メンタリングとは何をするものなのか」ということが書いてあったのだけど、ピックアップしていくと次のようなものだった。

  • メンタリングは、対話を通じて、メンタリングする人の思考力を一時的に貸し出し、思考の幅を広げていくことで、その人の歪んだ認知を補正し、次の行動を促し、成長させていく手法
    • 「思考のリファクタリング」の考え方を使い、対象となる人自体の考え方を少しずつ変えることで、問題解決の力を育む
  • メンタリングは「自ら考える自立的な人材」を作るために行う
  • メンターは「何か課題を指摘する」ための存在ではなく、課題に一緒に向き合い成長を支援するというコミットが求められる
  • メンタリングは自己説得を促し、知識の獲得を促す
    • 自己説得というのは、問題に対してメンティーが自分で問題に対するアクションを思いつくこと。
  • メンタリングでは、悩んでいる状態から脱出するために「次にとるべき行動」がはっきりするように促す必要がある
  • メンターはメンティに対して、「問題を解決してあげよう」という意識ではなく、「モヤモヤしていない問題=答えはまだわからないが明確次にすべき行動がわかる問題に変換してあげよう」と考えることが重要
  • メンターにとっての課題は「メンティを自立的な問題解決」に導くことであって、「メンティの課題を解決すること」ではない

結局メンタリングというのは、「メンターがメンティーの問題を解決してあげる」役割なのではなく、「メンティーが自身の問題を自身で解決できるように支援し、メンティーの成長を促す」役割という認識をもつ必要があるのだなと感じた。実際にメンタリングしている時の実感として、こちらからアドバイスした時は行動が起こりにくく、逆に「ああ、こうしたら良いかもしれませんね」とメンティーが言ったときは行動が起こりやすい。

もちろん急ぎで対応したい場合や、メンティーが対処しにくい問題については、メンターが積極的に問題解決を図れば良いと思った。なので、メンティーが解決手法を思いつくように支援するのと、こちらからアドバイスや他の部署に働きかけるなどメンターが問題解決するのの両方をうまく使い分けられると良いと感じた。

まとめ

今回は「エンジニアリング組織論への招待」を読んで、特に印象に残ったところをまとめてみた。メンタリングの章以外も非常に示唆に富む内容で非常に良い本だと思ったので、「エンジニアリング組織論への招待」は是非いろんな立場の人に読んで欲しい。

読書ノート

自分が印象に残ったところだけメモした読書ノート。Chapter2が面白すぎて異常に長いノートになった。

## Chapter1: 思考のリファクタリング
- エンジニアリングとは何か -> 「曖昧さ」を減らし、「具体性・明確さ」を増やす行為 305
- エンジニアリングで重要なのは「どうしたら効率よく不確実性を減らしていけるのか」 323 ◯
- 企業という組織は、組織全体を通じて、何かを実現するために、より曖昧な状態から具体的な状態に変化させるということを行っている 339
- 具体的で細かい指示をしないと動けない組織では、その組織のメンバーは小さな「不確実性」の削減しかできない。一方、抽象的で自由度のある指示でも動ける組織であれば、少ない指示で物事を実現できるので、より大きな「不確実性」の削減を行うことができる 339
    - 「具体的で細かい指示」を必要とする組織を「マイクロマネジメント型」の組織
    - 「抽象的で自由度のある指示」でも動ける組織を「自己組織化された」組織
- 人間にとって本質的に「わからないこと」は「未来」と「他人」だけ 390
    - 「未来」がわからないことの不確実性を「環境不確実性」という。頭でいくら考えてもわからないものなので、実際に行動し、実験して観察することで少しずつ確実になっていく
    - 「他人」がわからないことによる不確実性を「通信不確実性(コミュニケーション不確実性)」という。コミュニケーションを通じて不確実性を削減するしかない
        - 会話や書き残したことがすべて正しく伝わるとは限らない、伝わっても思ったように行動するとも限らない
    - 不確実性と対策の図 406
- 3つの思考様式である「論理的思考の盲点」「経験主義と仮説思考」「システム思考」 475
- 論理的思考では、「ルールと事象(考えのもとになる事実)を正しく認知できること」「正しく演繹できること」の両方を満たさないと正しく行えない 490
    - そのため、「非論理的に考えてしまう」瞬間を知ることが大事
- 日常的に発生する様々な問題に思いを悩ませているとき、「コントロールできないものをコントロールしようとしている」ため、いつまでたっても、問題解決の糸口が見つからないということがある 825 ◯
    - 「上司が自分の仕事を評価してくれない」というケースの場合のコントロールできるものできないもの 827
    - 問題を解決したいなら、コントロールできるもの・できないものを冷静に判断する必要がある 844
- 変化を「観測」できないものは、間接的にすらコントロールできる可能性がない 861 ◯
    - 新入社員の能力や内心は観測できない、新入社員の行動は観測できる
    - 「コントロールできるもの」を操作し、「観測できるもの」の結果をみることでしか、前に進むことができない 879
- 良いプログラマの条件になる問題解決の目 1240
    - 視野: あるポイントからその問題を眺めた時に同時に把握できる領域の広さ
    - 視座の高さ、低さ。抽象的 <-> 具体的
    - 視点の鋭さ、凡庸さ。どこに視点を合わせるか

## Chapter2: メンタリングの技術
- メンタリングは、対話を通じて、メンタリングする人の思考力を一時的に貸し出し、思考の幅を広げていくことで、その人の歪んだ認知を補正し、次の行動を促し、成長させていく手法 1416 ◯
    - 「思考のリファクタリング」の考え方を使い、対象となる人自体の考え方を少しずつ変えることで、問題解決の力を育む
    - メンタリングは「自ら考える人材を作る」テクニック 1478
    - メンターは「何か課題を指摘する」ための存在ではなく、課題に一緒に向き合い成長を支援するというコミットが求められる 1568
    - メンタリングは自己説得を促し、知識の獲得を促す 1604
    - メンタリングでは、悩んでいる状態から脱出するために「次にとるべき行動」がはっきりするように促す必要がある 1677
    - メンターはメンティに対して、「問題を解決してあげよう」という意識ではなく、「モヤモヤしていない問題=答えはまだわからないが明確次にすべき行動がわかる問題に変換してあげよう」と考えることが重要 1676
    - メンターは解けないパズルを「ひとりでも」解けるパズルに変換する 1694
        - 傾聴・可視化・リフレーミング
    - メンターにとっての課題は「メンティを自立的な問題解決」に導くことであって、「メンティの課題を解決すること」ではない 2026
    - メンタリングは自立的な人材を育むために行う 2398
- 障害の対応の際は、多くの人が目の前の課題に目が向いてしまいがちだが、そういったときには障害時のハンドリングを行う司令塔が必要 1478
- メンターとメンティの関係性を効率的にするための3つの条件 1532
    - 謙虚: お互いに弱さを見せられる
    - 敬意: お互いに敬意を持っている
    - 信頼: お互いにメンティの成長期待をもっている
- ピアメンター: 数年上の先輩が新入社員をサポートする形でとられる、比較的距離が近い、しかし、ロールモデルとなるほど尊敬されているわけではないという状況から始まるメンター関係 1568
    - 社内のメンターのイメージに近い
- メンタリングにおいて「成長の階段を上らせる」ためには、 1587
    - 課題を認識させる: 見えてない課題に自分から気づかせるように誘導
    - 課題に段階を作る: 成長の階段が大きい場合は一歩一歩進めるように段階を作り、成長実感を感じてもらう
    - 課題達成したくさせる: 大きな目標達成やゴールの認識をあわせるなどして、その課題のモチベーションを保てるようにする
- メンタリングでは他者説得よりも自己説得を重視し、その獲得を促す 1604
- 他者説得と自己説得の特徴 1623
    - 他者説得: 他人が答えを伝える、体感を伴わない、理解を確認できない
    - 自己説得: 他人が質問で促す、体感を伴う、行動の変化が発生しやすい
- 「悩む」と「考える」の違い 1640 ◯
    - 「悩んでいる」のは頭のなかでぐるぐると思考がまわり、もやもやが取れない状態。頑張っているように見えるわりに、結果が伴わない
    - 「考える」ときは、課題を書き出したり、具体化したりと何かと忙しく行動している。答えが出ていなくても次に何をしたら良いかは明確で、手が止まるというようなことはない
- メンタリングでは、「次にとるべき行動」がはっきりするように促す必要がある 1677
- メンターはメンティに対して、「問題を解決してあげよう」という意識ではなく、「モヤモヤしていない問題=答えはまだわからないが明確次にすべき行動がわかる問題に変換してあげよう」と考えることが重要 1676
- 問題は、感情的に固執している、客観視出来ていない、そもそも解けないという理由があると、解決できない 1694
- 解けないパズルを、「ひとりでも」解けるパズルに変換する 1694
    - 感情的に固執していて解けないので、「傾聴」する
    - 客観視出来ずに解けないので「可視化」する
    - そもそも解けない問題なので、前提を変える「リフレーミング」をする
- 傾聴は話を聞くだけでなく、効率的に頭のなかにいっぱいになった不安や迷いを「空」にするテクニック 1708
- 傾聴では相手を中心としながら、相手の思考が整理され前向きに考えられるように支援するように意識する 1727
    - 相手の感情への共感を言動で表す
    - 相手の話の内容を可視化する
    - 相手の思考の盲点を探索しながら質問をする
- 共感をして話を聞き出す「信号」 1727
    - うなずき、座り位置、表情(ミラーリング)、あいづちの打ち方
- 問題の可視化と明晰化 1801
    - 問題を客観的な問題に変換する工程
    - メンティの視線を、問題vs自分たちの構図とする
        - 問題の形をメンティとメンターが見れるものとして、ホワイトボードやノートに書き出す
    - 問題の可視化と明晰化はメンターとメンティの対話を通じて、「簡単な問題に変換する」ためのテクニック
- 問題の可視化と明晰化のためのテクニック 1851
    - 事実と意見は分ける 1851
    - 「解きたい問題は何か」「その答えの範囲はどこからどこまでか」ということのフォーカスを適切に絞る 1869
    - 解決策の、「選択肢」「比較軸」「評価方法」を明確にしていく 1906
- 認知フレームを別のフレームに変えていくことで解けない問題を解ける問題に変える 1941
    - 認知フレームを発見するキーワード: こちら系(この会社は、この人は)、あちら系(あの部署は、あの人は)、極端系(いつも、絶対に)、すべき系(常識的に、普通)、決めつけ系(どうせ、〜に違いない)
- 前提を問うような質問 1953
    - 〜が問題なのはそもそもなんででした?
    - 〜がないと具体的に何が困るんでした?
    - どうなったら解決されたといえる?
    - 良い解決策の条件は?
    - 一旦、この前提がなかったらどうなりますか?
    - 一番重要だと思うものはなんですか?
- 不安や悩みを抱えると、どんどんと周りの課題を巻き込んで、1つの大きな手の施しようのない大問題を頭に作り出す事があるので、そのときは「課題の分離」でメンティ自身の本当の課題を抽出する 1989
    - 質問例: あなたにとって具体的に何が問題か、あなたがコントロールできるものは何か、どうなればその具体的な問題は解消されたといえるのか
- メンターにとっての課題は「メンティを自立的な問題解決」に導くことであって、「メンティの課題を解決すること」ではない 2026
- 心理的安全性を高めることによる影響 2036
- 「対人リスク(=個々人の関係性が損なわれる可能性のある行動)」を積極的に取れる状態とは、相手との関係性は決して崩れることは無いだろうという確信がもてる状態のとき 2057
- メンタリングが機能する(問題解決が生まれる)時は、対人リスクを取り合って問題と自分たちという構図がうまれている時 2074
- メンタリングを効果的にするためには、お互いに「対人リスク」を積極的に取れる状態を構築する必要がある 2092
    - メンティの弱さ・失敗を開示してもらえる、自分の弱さ失敗を開示できる状態
- 関係性が破綻することはないという確信を相手に抱いてもらうためのテクニック 2111
    - アクノレッジメント: メンティ自身の存在を認めているというメッセージを発信し続ける
    - メンターから自分の弱さ失敗をメンティに伝えていく
    - ストーリーテリング: メンターが自分の失敗から成長できたことをうまく伝える
- アクノレッジメントの3段階 2129
    - 存在承認、行動承認、結果承認
- メンティの悩みをメンターが聞くだけでなく、メンターがメンティに相談して意見を求めるというのも「アクノレッジメント」の1つ 2163
- SMARTの原則 2302
- 能力は習慣の積分、習慣は行動の積分。行動や習慣はコントロールできるのでメンタリングの方法論で成長を促すことができる。積み重ねによって能力や成果に結びつく 2335
- 自立的な人材を育むためのメンタリングのために、以下の状態に導く必要がある 2398
    - 自分の気が付かなかった問題に気がつくようになる
    - 認知の歪みによる感情と問題の癒着を切り離せる
    - 答えではなく、次の一手を生み出す行動が取れるようになる
- 上記の状態に導くためにもっとも重要なのが「ゴール認識」 2398
    - 目標のようなものだが、達成できそうだが難しい高い目標のようなもの
    - ゴール認識は願望・義務の状態ではあまり行動が変化しないが、欲求・意志・必然というレベルになって初めて行動に変化が起こる 2416

## Chapter 3: アジャイルなチームの原理
- アジャイルな状態とは何か 2648
    - 情報の非対称性が小さい
    - 認知のゆがみが少ない
    - チームより小さい限定合理性が働かない
    - 対人リスクを取れていて心理的安全性が高い
    - 課題・不安に向き合い不確実性の削減が効率よくできている
    - チーム全体のゴール認識レベルが高い
- 暗黙知と形式知の知識が獲得されていく過程を表すSECIモデル 2793
    - 湧き上がってくる「暗黙知」が、「形式知」に代わり、その「形式知」が、組織全体に広がり「暗黙知」として根付いたとき、それらを土台にさらに新しい「暗黙知」が生まれてくるようなループが組織内において知識を広げ、深めていくために必要なプロセスである
- スクラムは要約すると「振り返りのためのフレームワーク」 2990
- スクラムのプロセスの骨子としては2つの学習ループ 2990
    - Howに関する学習ループ: 開発現場における改善を促すような「計画」と「振り返り」
    - Whatに関する学習ループ: プロダクトの意思決定者と共に行う、顧客に対して何を届けていくべきかを検証する
- リーンとは、ムダ・ムラ・ムリを減らしていくフレームワーク 3022
    - 減らすための重要な7つの原則 3022

## Chapter 4: 学習するチームと不確実性マネジメント
- 不確実性は3つに大別される。将来がわからないことから生じる方法不確実性と目的不確実性、他人とのコミュニケーションの失敗や不足によって生じる通信不確実性 3363
    - 方法不確実性への対応 -> スケジュール予測と見積もりの手法
    - 目的不確実性への対応 -> 要求と仮説検証の手法
    - 通信不確実性への対応 -> 振り返りの手法
- プロジェクトにかかる期間は、総作業時間の立方根に比例する 3393
- スケジュールマネジメントのためには、理想工期、制約スラック、プロジェクトバッファの3つを考える必要がある 3393

## Chapter 5: 技術組織の力学とアーキテクチャ
- 個々人の意識の力は弱く、「空気」のようなものにより気が付かないうちに悪い方向に組織を導いてしまうことがある 4207
- 「空気」に支配されないように、構造的な問題の解決に取り組む必要がある 4207
    - 権限の移譲と期待値調整
    - 適切な組織・コミュニケーション・外部リソース調達の設計
    - 全体感のあるゴール設定と透明性の確保
    - 技術的負債の見える化
- 権限委譲の段階 4302 ◯
    - 命令する、説得する、相談する、合意する、助言する、尋ねる、委任する
- デリゲーションポーカー 4302
    - 権限委譲のレベルを上司-部下間や関係者の間で合意を取る手法
- 権限と責任という観点から見た組織設計のポイント 4345
    - 明示的な権限と責任の移譲を行う
    - 権限と責任の不一致をなくす
    - 権限同士の衝突を最小にする
    - 権限の衝突解消レベルを最小にする
- コミュニケーションパターンに基づいた組織を再設計するための指針 4358
    - 相互依存の同一化
    - 事業とプロセスの同一化
    - 戦略の同一化
- 組織のパフォーマンスを考えるとき、権限が適切に移譲され、その責任を果たすことができる組織構造と不断のコミュニケーションによる期待値調整こそが、情報処理能力の高い組織の最も重要な視点 4358

自作ScalaライブラリをMaven Centralにリリースする手順メモ

最近Scalaの勉強も兼ねてjoda-time-fakeというScalaライブラリをMaven Central Repositoryにリリースしてみた。今回はその手順を忘れないようにメモとして残しておく。

今回の手順でできたこと

  • com.github.shibayu36 organizationにjoda-time-fakeとしてライブラリをリリースする
  • Scalaの2.11と2.12でクロスコンパイルして両方共リリースする
  • 最終的にsbt-releaseでリリースを自動化できるようにする

参考資料

自作のScalaライブラリをMaven Central Repositoryにリリースする - Qiitaが一番参考になった。少しハマりどころはあったものの、これを見て一つずつやっていったらリリースまで出来たので感謝。今回の僕の記事はクロスコンパイルして複数バージョン上げるという点が違うだけで、この記事とほぼ同様なものになっている。

またsbtの公式ドキュメントとしてThis page has movedも参考になった。sonatypeの公式ドキュメントとしてOSSRH Guide - The Central Repository Documentationも参考にしたけど、こちらは正直何をしていったら良いか分かりづらかった。

build.sbtや構成などは以下の二つのレポジトリを参考にした。

環境

  • macOS High Sierra 10.13.3
  • scala 2.12.4 & 2.11.12
  • sbt 1.1.1
  • sbt-sonatype 2.3
  • sbt-pgp 1.1.1
  • sbt-release 1.0.8

作業の流れ

  • Sonatype JIRA にアカウントを登録する
  • Sonatype JIRAにNew Project Issueを作成する
  • sbt-pgp を使ってGPGの公開鍵を keyserver に登録する
  • build.sbtでコンパイルやリリースの設定をする
  • sbt-sonatypeでリリースをする
  • 初回リリースしたらProject Issueにコメント
  • リリース確認
  • 【オプション】sbt-releaseによるリリース自動化

Sonatype JIRA にアカウントを登録する

https://qiita.com/kiris/items/b043a7582c22110d7097#sonatype-jira-%E3%81%AB%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%82%92%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B のとおり、https://issues.sonatype.org/secure/Signup!default.jspa からアカウントを作る。

後のリリース作業のために、$HOME/.sbt/1.0/sonatype.sbtにその認証情報を記述。

credentials += Credentials("Sonatype Nexus Repository Manager",
  "oss.sonatype.org",
  "<Sonatype JIRAのユーザー名>",
  "<Sonatype JIRAのパスワード>")

Sonatype JIRAにNew Project Issueを作成する

ユーザー登録したら、アップロードするorganizationを作ってもらうために、Sonatype JIRAにNew Project Issueを作成する。https://qiita.com/kiris/items/b043a7582c22110d7097#sonatype-jira%E3%81%ABnew-project-issue%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B のようにやったらできるはず。今回のは https://issues.sonatype.org/browse/OSSRH-38391 なので、参考にどうぞ。

僕はNew Issueしたらすぐにcom.github.shibayu36を用意してもらえた。

sbt-pgp を使ってGPGの公開鍵を keyserver に登録する

リリースするためには公開鍵を何処かに登録しておかないといけない。sbt-pgpを使うと簡単に登録できる。

~/.sbt/1.0/plugins/gpg.sbtを追加

addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1")

コマンドを実行

$ sbt
> set useGpg := true
> set pgpReadOnly := false
> pgp-cmd gen-key
> pgp-cmd list-keys

この時ちょっとはまったことがあって、pgp-cmd gen-keyした時にエラーが起こって落ちた。

> pgp-cmd gen-key
Failed to run pgp-cmd: GeneratePgpKey().   Please report this issue at http://github.com/sbt/sbt-pgp/issues

これは https://github.com/sbt/sbt-pgp/issues/55 を参考にして、show */*:pgpSecretRingshow */*:pgpPublicRingコマンドを打って出てきたファイルをrmしたら直った。中身は空だったのでたぶん一度set pgpReadOnly := falseせずにpgp-cmd gen-keyをしてしまっていたので、その時に空ファイルができてしまっていたのだと思う。

build.sbtでコンパイルやリリースの設定をする

続いてbuild.sbtでコンパイルやリリースの設定をする。https://github.com/shibayu36/joda-time-fake-scala/blob/0.0.1/build.sbt を参考に。基本的には

あたりを設定すると良い。

build.sbtはこんな感じ。

name := "joda-time-fake"

organization := "com.github.shibayu36"

version := "0.0.1"

scalaVersion := "2.12.4"
crossScalaVersions := Seq("2.11.12", "2.12.4")

libraryDependencies ++= Seq(
  "joda-time" % "joda-time" % "2.9.9",
  "org.scalatest" %% "scalatest" % "3.0.5" % "test"
)

// Mavenへリリースするための設定
publishMavenStyle := true
publishArtifact in Test := false
pomIncludeRepository := { _ => false }
publishTo := {
  val nexus = "https://oss.sonatype.org/"
  if (isSnapshot.value)
    Some("snapshots" at nexus + "content/repositories/snapshots")
  else
    Some("releases"  at nexus + "service/local/staging/deploy/maven2")
}
// リリース設定はここまではテンプレで書いておく
// 以下は自分のライブラリに合わせて書いていく
pomExtra := (
  <url>https://github.com/shibayu36/joda-time-fake-scala</url>
  <licenses>
    <license>
      <name>The Apache Software License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  <scm>
    <url>https://github.com/shibayu36/joda-time-fake-scala</url>
    <connection>scm:git:https://github.com/shibayu36/joda-time-fake-scala.git</connection>
  </scm>
  <developers>
    <developer>
      <id>shibayu36</id>
      <name>Yuki Shibazaki</name>
      <url>https://github.com/shibayu36/</url>
    </developer>
  </developers>
)

pomの設定は、https://qiita.com/kiris/items/b043a7582c22110d7097#project-rootbuildsbt のように、sbtの機能で書いてもいいんだけれど、まあpomExtraで全部書いたらいいかという気分になったので、そのようにしている。

sbt-sonatypeでリリースをする

ここまで設定できたら後はリリースするだけ。以下のコマンドを叩くと複数のScalaのバージョンでコンパイルしてリリースできる。https://github.com/xerial/sbt-sonatype

$ sbt
> + clean
> + compile
> + test
> + publishSigned
> sonatypeReleaseAll

ここで注意するのは、複数Scalaのバージョンでリリースしたいからといって+ sonatypeReleaseを実行しないこと。これを行うとなんかめちゃくちゃになって、sonatypeからISEが返ってくるようになってしまい、結局手動でsonatypeを見に行って変なやつをcloseするという作業が必要になった。

ロスコンパイル環境では、sonatypeReleaseAllを使うと良いということだった。

初回リリースしたらProject Issueにコメント

初回リリースしたら、Central syncをアクティブにしてもらうために、「Sonatype JIRAにNew Project Issueを作成する」で作ったIssueにコメントする。https://issues.sonatype.org/browse/OSSRH-38391?focusedCommentId=469046&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-469046 の感じ。

リリース確認

あとはMaven Central Repositoryで検索して、自分のライブラリがアップロードされていることが確認できたらOK。反映には少し時間がかかったので気長に待つと良い。僕はこんな感じで複数Scalaバージョンでリリースできてたので大丈夫そうだった。


これでひとまず作業終了。お疲れ様でした。

【オプション】sbt-releaseによるリリース自動化

あとは次回リリースに備えて、gitのタグ打ちとかいろんなものを自動化しておきたかったので、sbt-releaseの設定をしておいた。

まずproject/plugins.sbtにプラグインの依存を追加。

addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.8")

続いてbuild.sbtの変更。https://github.com/sbt/sbt-release によると、バージョン情報はversion.sbtに記録されるということなので、build.sbtからはversion := "0.0.1"の記述を削除しておく。その後build.sbtに以下のように追記。

// Release
import ReleaseTransformations._

releaseCrossBuild := true // true if you cross-build the project for multiple Scala versions
releaseProcess := Seq[ReleaseStep](
  checkSnapshotDependencies,
  inquireVersions,
  runClean,
  runTest,
  setReleaseVersion,
  commitReleaseVersion,
  tagRelease,
  // For non cross-build projects, use releaseStepCommand("publishSigned")
  releaseStepCommandAndRemaining("+publishSigned"),
  setNextVersion,
  commitNextVersion,
  releaseStepCommand("sonatypeReleaseAll"),
  pushChanges
)

あとはsbt releaseするだけ!これでcompileやtest、gitのタグ打ち、リリースなど全部一気にやってくれる。

$ sbt
> release

最終的なbuild.sbtはhttps://github.com/shibayu36/joda-time-fake-scala/blob/v0.0.2/build.sbt

自作のScalaライブラリをpublishLocalしながら手元でリリース前に最終確認する

joda-time-fakeを作りながら、リリースする前に手元で最終確認したいと思い、やり方を調べていた。調べてみると、publishLocalというものを使えば、ローカルのivy repositoryに成果物をpublishすることができ、そうすることで他のプロジェクトから参照できるようになるようだ。これを使えば手元で最終確認できる。

自分のライブラリをpublishLocalする

https://www.scala-sbt.org/1.x/docs/Publishing.html#Publishing+LocallysbtでpublishLocalしたjarを取り込んで開発サイクルを回す - Qiita にやり方が書いてあった。

まず自分のライブラリのプロジェクトでsbtを起動し、publishLocalする。

$ sbt
sbt:joda-time-fake> +publishLocal
[info] Setting Scala version to 2.12.4 on 1 projects.
[info] Reapplying settings...
[info] Set current project to joda-time-fake (in build file:/Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/)
[info] Packaging /Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/target/scala-2.12/joda-time-fake_2.12-0.0.3-SNAPSHOT-sources.jar ...
[info] Updating ...
[info] Done packaging.
[info] Wrote /Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/target/scala-2.12/joda-time-fake_2.12-0.0.3-SNAPSHOT.pom
[info] Done updating.
[info] Main Scala API documentation to /Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/target/scala-2.12/api...
[info] Compiling 1 Scala source to /Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/target/scala-2.12/classes ...
model contains 7 documentable templates
[info] Main Scala API documentation successful.
[info] Packaging /Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/target/scala-2.12/joda-time-fake_2.12-0.0.3-SNAPSHOT-javadoc.jar ...
[info] Done packaging.
[info] Done compiling.
[info] Packaging /Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/target/scala-2.12/joda-time-fake_2.12-0.0.3-SNAPSHOT.jar ...
[info] Done packaging.
[info] :: delivering :: com.github.shibayu36#joda-time-fake_2.12;0.0.3-SNAPSHOT :: 0.0.3-SNAPSHOT :: integration :: Sun Mar 25 10:00:36 JST 2018
[info]  delivering ivy file to /Users/shibayu36/development/src/github.com/shibayu36/joda-time-fake-scala/target/scala-2.12/ivy-0.0.3-SNAPSHOT.xml
[info]  published joda-time-fake_2.12 to /Users/shibayu36/.ivy2/local/com.github.shibayu36/joda-time-fake_2.12/0.0.3-SNAPSHOT/poms/joda-time-fake_2.12.pom
[info]  published joda-time-fake_2.12 to /Users/shibayu36/.ivy2/local/com.github.shibayu36/joda-time-fake_2.12/0.0.3-SNAPSHOT/jars/joda-time-fake_2.12.jar
[info]  published joda-time-fake_2.12 to /Users/shibayu36/.ivy2/local/com.github.shibayu36/joda-time-fake_2.12/0.0.3-SNAPSHOT/srcs/joda-time-fake_2.12-sources.jar
[info]  published joda-time-fake_2.12 to /Users/shibayu36/.ivy2/local/com.github.shibayu36/joda-time-fake_2.12/0.0.3-SNAPSHOT/docs/joda-time-fake_2.12-javadoc.jar
[info]  published ivy to /Users/shibayu36/.ivy2/local/com.github.shibayu36/joda-time-fake_2.12/0.0.3-SNAPSHOT/ivys/ivy.xml
[success] Total time: 2 s, completed Mar 25, 2018 10:00:36 AM

これにより、手元のivy repositoryにpublishできた。ログを見ていると、~/.ivy2/localがivy repositoryとして機能するようだ。実際に確認してみると

$ ls ~/.ivy2/local/com.github.shibayu36/
joda-time-fake_2.11 joda-time-fake_2.12
$ ls ~/.ivy2/local/com.github.shibayu36/joda-time-fake_2.11
0.0.3-SNAPSHOT
$ tree ~/.ivy2/local/com.github.shibayu36/joda-time-fake_2.11/0.0.3-SNAPSHOT
/Users/shibayu36/.ivy2/local/com.github.shibayu36/joda-time-fake_2.11/0.0.3-SNAPSHOT
├── docs
│   ├── joda-time-fake_2.11-javadoc.jar
│   ├── joda-time-fake_2.11-javadoc.jar.md5
│   └── joda-time-fake_2.11-javadoc.jar.sha1
├── ivys
│   ├── ivy.xml
│   ├── ivy.xml.md5
│   └── ivy.xml.sha1
├── jars
│   ├── joda-time-fake_2.11.jar
│   ├── joda-time-fake_2.11.jar.md5
│   └── joda-time-fake_2.11.jar.sha1
├── poms
│   ├── joda-time-fake_2.11.pom
│   ├── joda-time-fake_2.11.pom.md5
│   └── joda-time-fake_2.11.pom.sha1
└── srcs
    ├── joda-time-fake_2.11-sources.jar
    ├── joda-time-fake_2.11-sources.jar.md5
    └── joda-time-fake_2.11-sources.jar.sha1

のようにjarファイルやjavadocなどが作られていることが分かる。

ローカルのレポジトリのライブラリを利用する

あとは別プロジェクトでbuild.sbtのlibraryDependenciesに追加すると、ローカルのレポジトリの内容を見てくれる。

build.sbtに追加

libraryDependencies ++= Seq(
  "com.github.shibayu36" %% "joda-time-fake" % "0.0.3-SNAPSHOT"
)

こんな感じのテストファイルを用意する。

import com.github.shibayu36.jodatimefake.FakeTimer
import org.joda.time.DateTime

object FakeTimerExample {
  def main(args: Array[String]): Unit = {
    FakeTimer.fake(1515974400000L) {
      println(DateTime.now.toString)
    }
    FakeTimer.fakeWithTimer(1515974400000L) { t =>

    }
    println(DateTime.now.toString)
  }
}

実行して確認。

$ sbt
> runMain FakeTimerExample
[info] Running FakeTimerExample
2018-01-15T09:00:00.000+09:00
2018-03-25T10:09:26.493+09:00
[success] Total time: 0 s, completed Mar 25, 2018 10:09:26 AM

これでいろいろ確認することが出来た。

versionにはSNAPSHOTのpostfixをつけよう

https://www.scala-sbt.org/1.x/docs/Publishing.html#Publishing+Locallyに書いてあるとおりなのだけど、SNAPSHOTのpostfixがついていないとキャッシュが使われてpublishLocalした内容が使われなかったりする。そのため、0.0.3-SNAPSHOTのようにSNAPSHOTのpostfixをつけるようにしたほうが良さそうであった。

まとめ

今回は自作のScalaライブラリをリリースする前に確認するため、publishLocalを使った方法について試してメモしてみた。これで実際に別プロジェクトからうまく使えるかや、scaladocの表示などを確認できるようになって良かった。

また今回のことを調べた時についでにsbtが依存管理につかっているivyについて調べたので、それについてはまた別の機会にブログで公開しようと思う。