Slide 1

Slide 1 text

Scala をインタラクティブにデバッグする方法あれこれ Lightning Talk@2023/10/13 Scalaわいわい勉強会

Slide 2

Slide 2 text

WHOAMI GitHub: i10416 X(Twitter): @by110416 Scala & Rust developer 最近の Scala OSS コントリビューション https://github.com/neotypes/neotypes/releases/tag/v1.0.0-M3

Slide 3

Slide 3 text

前提:次のようなパッケージをデバッグする想定で説明する. // src/main/scala/Lib.scala package com.example object Lib { def doSomething(): Int = 42 } // build.sbt ThisBuild / organization := "com.example" val lib = project.in(file(".")) .settings( scalaVersion := "3.3.1", version := "0.1.0-SNAPSHOT", libraryDependencies ++= Seq( "org.scalameta" %% "munit" % "1.0.0-M7" % Test ) )

Slide 4

Slide 4 text

Table of Contents sbt console test publishLocal & import from local repository import from private maven repository

Slide 5

Slide 5 text

予習: sbt shell と sbt console sbt shell: 通常のシェル(zsh、fish や bash)で sbt と入力したときに開始される sbt の インタラクティブなインターフェース. sbt console: sbt shell から呼び出せる Scala REPL 環境 以下の例ではコードブロックの行頭が sbt> と書かれている場合 sbt shell 内でのコマンド実 行を、scala> と書かれている場合は Scala REPL 内でのコマンド実行を意味する.

Slide 6

Slide 6 text

sbt console でデバッグ sbt console scala>import com.example.Lib scala>Lib.doSomething() res0: Int = 42 scala>

Slide 7

Slide 7 text

sbt console でデバッグ 複数行の入力 scala>:paste // Entering paste mode (ctrl-D to finish) object DeepThought { val theAnswer = 42 } // ctrl-D

Slide 8

Slide 8 text

sbt console でデバッグ ファイルの読み込み // DeepThought.scala object DeepThought { val theAnswer = 42 } scala>:load DeepThought.scala

Slide 9

Slide 9 text

sbt console でデバッグ: 初期化コマンドの設定 console/initialCommands を設定すれば sbt console の初期化処理を追加できる. 毎回特定のモジュールやライブラリやインポートする場合は設定しておくと楽. // build.sbt val lib = project.in(file(".")) .settings( scalaVersion := "3.3.1", version := "0.1.0-SNAPSHOT", console / initialCommands := "import java.nio.file._", libraryDependencies ++= Seq( "org.scalameta" %% "munit" % "1.0.0-M7" % Test ) )

Slide 10

Slide 10 text

sbt console でデバッグ: multi modules 特定のサブプロジェクトのデバッグをしたい場合は sbt shell 内で projects コマンドを実行し サブプロジェクトの名前を確認し projectname/console(下の例では sbt contrib/console ) とすればいい. ├── build.sbt ├── contrib/src/main/scala/... └── core/src/main/scala/... // build.sbt lazy val core = project.in(file("core")) ... lazy val contrib = project.in(file("contrib")) ...

Slide 11

Slide 11 text

sbt console でデバッグ: Pros & Cons Pros 楽. セットアップや(ローカル)リリースが必要ない. 期待する動作がそこまで定まっていなくてもいい. Cons パッケージやアプリケーション全体をデバッグするのには向かない. JS や Native ではデバッグできない. コンパイルが通っている必要がある(consoleQuick を使えば回避できる)

Slide 12

Slide 12 text

test(& sbt testOnly) でデバッグ // src/test/scala/Test.scala package com.example class Test extends munit.FunSuite { test("Lib.doSomething returns 42") { assertEquals(Lib.doSomething, 42) } } test sbt>test testOnly sbt>testOnly com.example.Test

Slide 13

Slide 13 text

watch ~ をコマンドの前につけるとソースコードの変更を監視して、変更があれば自動でコマンド を再実行してくれる. sbt>~testOnly com.example.Test

Slide 14

Slide 14 text

test でデバッグ: Pros & Cons Pros JS や Native でも動作確認できる. console より IDE のサポートを活用できる. Cons コンパイルを通さないといけない. テストを書かないといけない. ユニットテストが書きにくいコードベースだと辛い. ある程度期待する動作が定まっている必要がある.

Slide 15

Slide 15 text

sbt publishLocal でデバッグ publishLocal コマンドを実行すると、~/.ivy2/local/com.example/lib/0.1.0-SNAPSHOT のよ うなローカルのファイルシステム上にパッケージを配布できる. sbt>publishLocal ローカルに配布した後は通常のパッケージのように sbt の libraryDependencies や Scala CLI の using dep ディレクティブ、ammonite のマジックインポートで利用できる.

Slide 16

Slide 16 text

import from sbt project ローカルに配布したパッケージは通常のパッケージと同じように利用できる. // build.sbt libraryDependencies ++= Seq( "com.example::lib:0.1.0-SNAPSHOT" )

Slide 17

Slide 17 text

import from Scala CLI Scala CLI では using dep でパッケージを読み込める. using dep はデフォルトで maven のレポジトリとローカルのレポジトリからパッケージを探す. //> using dep "com.example::lib:0.1.0-SNAPSHOT" import com.example.Lib @main def run = println(Lib.doSomething()) //=> 42

Slide 18

Slide 18 text

import from ammonite REPL ammonite REPL では $ivy. のマジックインポートを使ってパッケージを読み込める. @ import $ivy.`com.example::lib:0.1.0-SNAPSHOT` @ import com.example.Lib @ Lib.doSomething() res0: Int = 42 @

Slide 19

Slide 19 text

sbt publishLocal でデバッグ: Pros & Cons Pros ライブラリ全体の動作確認をできる. Cons 配布+新しいプロジェクトを作成しないといけないのでやや手間. バージョンでキャッシュされるのでバージョンを上げるかコミットハッシュなどを付与 する必要がある.

Slide 20

Slide 20 text

Private な Maven レポジトリに配布されたパッケージをデバッグする 代表的なのは社内レポジトリから private パッケージを取得するユースケース. ここでは GCP の asia-northeast1 の example-project プロジェクトに example という Artifact Registry がある想定で説明する. GCP を例にするが基本的なアイディアは同じ(...なはず)

Slide 21

Slide 21 text

sbt project から GCP Artifact Registry にアクセスする sbt gcs resolver を利用する. この sbt plugin は GCS や Artifact Registry へのパッケージの配布・取得機能を提供する. // project/plugins.sbt addSbtPlugin("org.latestbit" % "sbt-gcs-plugin" % "1.8.0") デフォルトの認証情報を設定しておけば plugin がよしなに読み取ってくれる. gcloud auth application-default login

Slide 22

Slide 22 text

sbt project から GCP Artifact Registry にアクセスする // build.sbt resolvers += "My Maven Artifact Registry" at "artifactregistry://asia-northeast1-maven.pkg.dev/example-project/example" libraryDependencies ++= Seq( "com.example::lib:0.1.0-SNAPSHOT" )

Slide 23

Slide 23 text

Scala CLI から GCP Artifact Registry にアクセスする 認証情報を設定する. repository のホストが asia-northeast1-maven.pkg.dev の時に指定した 認証情報が使われる. scala-cli --power config \ repositories.credentials "asia-northeast1-maven.pkg.dev" \ value:_json_key_base64 value:eyXXInR5cXXiOiXXc2..... value::eyXXInR5cXXiOiXXc2..... の部分はサービスアカウント has-access-to-artifact- [email protected] のJSON鍵ファイルを base64 エンコ ードしたもの.

Slide 24

Slide 24 text

Scala CLI から GCP Artifact Registry にアクセスする using repository で maven やローカル以外のレポジトリを追加できる. //> using repository "https://asia-northeast1-maven.pkg.dev/examle-project/example" //> using dep "com.example::lib:0.1.0-SNAPSHOT" import com.example._ @main def run = println(Lib.doSomething())

Slide 25

Slide 25 text

Scala CLI から認証付きレポジトリにアクセスする scala-cli --power config \ repositories.credentials \ GitHub Packages などの認証付きレポジトリも同様に利用できる. scala-cli --power config \ repositories.credentials "maven.pkg.github.com" \ value:PrivateToken env:GITHUB_TOKEN /> using repository "https://maven.pkg.github.com//_"

Slide 26

Slide 26 text

Misc remove/unset scala-cli --power config --remove repositories.credentials coursier fetch cs fetch -r https://asia-northeast1-maven.pkg.dev/example-project/example \ --credentials "asia-northeast1-maven.pkg.dev _json_key_base64:eyXXInR5cXXiOiXXc2....." sbt help sbt> help help

Slide 27

Slide 27 text

Refs https://cloud.google.com/artifact-registry/docs/java/authentication?hl=ja#auth- password https://github.com/abdolence/sbt-gcs-resolver https://scala-cli.virtuslab.org/docs/commands/config/ https://www.reddit.com/r/scala/comments/175cm36/looking_for_a_working_example _of_import_from/