Clojure CLIでClojarsにデプロイする

[Clojure CLI](https://clojure.org/guides/deps_and_cli)は依存解決を行ってくれるコマンドラインツールで、Clojure 1.9とともに公式からリリースされました。これまで広く使われてきたLeiningenやBootに近い立ち位置のツールです。coreやcontrib系のプロジェクトではすでにClojure CLIが使われるようになっており、third-partyのライブラリ等でも徐々に使われるようになってきています。

多くのthird-party製のClojureライブラリは、[Clojars](https://clojars.org/)というMaven Repositoryにデプロイされています。Leiningenでは `lein deploy clojars` とするだけで簡単にデプロイできましたが、Clojure CLI自体にはそのような機能はありません。

自分が作っているClojureライブラリのプロジェクトをClojure CLIに移行したいと思い、Clojure CLIでClojarsへデプロイする方法について調べてみました。

## Clojure CLI + Leiningen

一つ目は、Leiningenを併用する方法です。開発にはClojure CLIを用い、デプロイにだけLeiningenを使います。

```
example/
├── deps.edn
├── project.clj
└── src/
    └── example/
        └── core.clj
```

`deps.edn` に加えて `project.clj` を用意し、バージョンや説明などは `project.clj` に書きます。

```clj
(defproject example "0.1.0-SNAPSHOT"
  :description "FIXME"
  :url "https://github.com/example/example"
  :license {:name "The MIT License"
            :url "https://opensource.org/licenses/MIT"}
  :dependencies [[org.clojure/clojure "1.9.0"]])
```

Clojarsへデプロイします。

```console
$ lein deploy clojars
```

簡単ですね。これまで主にLeiningenを使ってきた人なら、多分これが一番楽だと思います。

欠点は、

- Leiningenもインストールしておく必要がある
- 依存ライブラリを `deps.edn`, `project.clj` の2箇所に書く必要がある

といったところでしょうか。

## Clojure CLI + Boot

これまでBootを使ってきたなら、 `build.boot` を加えましょう。

```
example/
├── build.boot
├── deps.edn
└── src/
    └── example/
        └── core.clj
```

デプロイ用の情報を `build.boot` に書きます。

```clj
(set-env!
 :dependencies '[[org.clojure/clojure "1.9.0"]]
 :repositories [["clojars" {:url "https://clojars.org/repo/"
                            :username (System/getenv "CLOJARS_USER")
                            :password (System/getenv "CLOJARS_PASS")}]])

(task-options!
 pom {:project 'example
      :version "0.1.0-SNAPSHOT"
      :description "Deploy to Clojars with Clojure CLI + Boot"
      :url "https://example.com"
      :scm {:url "https://github.com/example/example"}
      :license {"The MIT License" "https://opensource.org/licenses/MIT"}})

(task-options! push {:repo "clojars"})
```

Clojarsへデプロイします。

```console
$ CLOJARS_USERNAME=<username> CLOJARS_PASSWORD=<password> boot pom jar push
```

Bootに置き換えただけなので欠点は同じく、

- Bootもインストールしておく必要がある
- 依存ライブラリを `deps.edn`, `built.boot` の2箇所に書く必要がある

となります。

## Clojure CLI + Maven

ClojarsはMavenリポジトリなので、Mavenを併用する方法もあります。ClojureのcontribライブラリなどはMavenを使っているようです。

`pom.xml` を追加します。

```
example/
├── deps.edn
├── pom.xml
└── src/
    └── example/
        └── core.clj
```

[clojure-maven-plugin](https://github.com/talios/clojure-maven-plugin)を使って、MavenからClojureコードをビルドできるようにします。

```xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>example</groupId>
  <artifactId>example</artifactId>
  <version>0.1.0-SNAPSHOT</version>
  <name>example</name>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>org.clojure</groupId>
      <artifactId>clojure</artifactId>
      <version>1.9.0</version>
    </dependency>
  </dependencies>

  <repositories>
    <repository>
      <id>clojars</id>
      <url>https://repo.clojars.org/</url>
    </repository>
  </repositories>

  <distributionManagement>
    <repository>
      <id>clojars</id>
      <name>Clojars repository</name>
      <url>https://clojars.org/repo</url>
    </repository>
  </distributionManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>com.theoryinpractise</groupId>
        <artifactId>clojure-maven-plugin</artifactId>
        <version>1.8.1</version>
        <extensions>true</extensions>
        <configuration>
          <sourceDirectories>
            <sourceDirectory>src</sourceDirectory>
          </sourceDirectories>
          <temporaryOutputDirectory>true</temporaryOutputDirectory>
        </configuration>
        <executions>
          <execution>
            <id>clojure-compile</id>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
```

Clojarsへデプロイします。

```console
$ mvn deploy
```

`deps.edn` のdependencyを更新した場合は、 Clojure CLIから `pom.xml` を更新することができます。

```console
$ clj -Spom
```

dependency以外の設定は保持されるため、何度も手動で編集する必要はありません。

欠点は、

- Mavenもインストールしておく必要がある
- 依存ライブラリを変更したら `clj -Spom` を再実行する必要がある

でしょうか。

## pack.alpha + Maven

さて、ここまではbuild, pushともにLeiningen等の別ツールが行っており、2つのツールがほとんど独立して使われていました。次は、buildはClojure CLIから行い、pushだけをMavenに任せます。

Clojure CLIのパッケージングツール[pack.alpha](https://github.com/juxt/pack.alpha)とMavenを使う方法です。

pack.alphaを使えば、Clojure CLIからjarファイルを作成することができます。（他にもいろいろ作れます）

```
example/
├── deps.edn
└── src/
    └── example/
        └── core.clj
```

`deps.edn` に以下のようにaliasを定義しておきます。

```clj
{:aliases {:jar {:extra-deps {pack/pack.alpha
                              {:git/url "https://github.com/juxt/pack.alpha.git"
                               :sha "a5d64ce2fe48f87445f35a2743a94ca9a2e3ca94"}}
                 :main-opts ["-m" "mach.pack.alpha.skinny" "--no-libs"
                             "--project-path" "example.jar"]}}}
```

```console
$ clj -Ajar
```

によって、 `example.jar` というjarファイルが作成されます。

```console
$ clj -Spom
```

として、 `deps.edn` をもとに `pom.xml` を自動生成します。

`pom.xml` を手動で編集し、バージョンや説明、URLなどを書き加えます。 `deps.edn` で依存ライブラリを変更したときは、再度 `clj -Spom` を実行します。2回目以降は依存ライブラリの更新だけが反映されるため、何度も手動で編集する必要はありません。

そして、Mavenを使ってClojarsにデプロイします。

```console
$ mvn deploy:deploy-file -Dfile=example.jar -DrepositoryId=clojars -Durl=https://clojars.org/repo -DpomFile=pom.xml
```

欠点は、

- Mavenもインストールしておく必要がある
- 依存ライブラリを変更したら `clj -Spom` を再実行する必要がある

でしょうか。

## pack.alpha + deps-deploy

最後に、Clojure CLIのみでbuildからpushまで完結させる方法です。

pack.alphaに加えて、[deps-deploy](https://github.com/slipset/deps-deploy)を使います。

```
example/
├── deps.edn
└── src/
    └── example/
        └── core.clj
```

`deps.edn` は以下のようにしておきます。

```clj
{:aliases {:jar {:extra-deps {pack/pack.alpha
                              {:git/url "https://github.com/juxt/pack.alpha.git"
                               :sha "a5d64ce2fe48f87445f35a2743a94ca9a2e3ca94"}}
                 :main-opts ["-m" "mach.pack.alpha.skinny" "--no-libs"
                             "--project-path" "example.jar"]}
           :deploy {:extra-deps {deps-deploy {:mvn/version "RELEASE"}}
                    :main-opts ["-m" "deps-deploy.deps-deploy" "deploy"
                                "example.jar"]}}}
```

jarファイルと `pom.xml` を作ります。初回は `pom.xml` の編集も忘れずに行いましょう。

```console
$ clj -Ajar
$ clj -Spom
```

そして、deps-deployを使ってClojarsへデプロイします。

```console
$ CLOJARS_USERNAME=<username> CLOJARS_PASSWORD=<password> clj -Adeploy
```

Clojure CLI以外のツールをインストールしておく必要がない点が良いですね。

欠点は、

- 依存ライブラリを変更したら `clj -Spom` を再実行する必要がある
- deps-deployがあまりこなれていない気がする（GPG signingなどできない）

でしょうか。

## おわりに

Clojure CLIをメインに開発を進める場合に、Clojarsへデプロイする方法をいくつか紹介しました。

いずれの方法においても、dependency変更時には複数ファイルの編集や `pom.xml` の再生成が必要となり、LeiningenやBootだけのプロジェクトと比べて少しステップが増えてしまう、というのが現状のようです。個々のタスクごとに小さいツールが存在するのはClojureらしいともいえますが、ライブラリのリリース時などにミスが発生しそうではあります。

デファクトスタンダードはまだなさそうなので、自分の好きな方法でデプロイすれば良いかと思います。これを機に、私も自分が作っているライブラリなどをClojure CLIに移行していきたいと考えています。