Jenkins Pipelineのpipeline {...}の"pipeline"と"{...}"は何か(Groovy初心者向け、他言語経験者向け)

# 背景
お仕事でJenkins Pipelineを書くことになりました。早速サンプルをコピーし、Hello Worldが表示されてうれしい一方、こんな疑問が。

この、`pipeline`と`{...}`とは何なのか？どういう文法規則なのか？

```groovy:Jenkinsfile
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'echo "Hello World"'
            }
        }
    }
}
```

Jenkinsfile自体は単なるデータ構造(JSONやYAMLの仲間)に見えます。ということは何か規則があるはずです。しかしそれが書かれているページが見つからない。
職場のつよつよエンジニアに聞いたところ、JenkinsfileはGroovyのプログラムということが分かりました。

その後自分で調べたことと、他の言語との対比を書いておきます。

# "pipeline"と"{...}"は何かの答え

`pipeline`は**メソッド名**
`{...}`は**クロージャ**

`pipeline {...}`は、`pipeline`メソッドに、``{...}``というクロージャを**引数**として渡す、という構文です。

`pipeline`はメソッドなので、Jenkinsfileの実行時に

```groovy
def pipeline(Closure cl) {
    // 前処理
    // クロージャを呼び出す
    cl();
    // 後処理
}
```

のように定義されたメソッドが(どこかから)読み込まれています。(上記コードは正確には少し違います。補足を参照してください)

# もっと詳しく
### groovyのメソッド呼び出し
上記の`pipeline`の呼び出しを冗長に書くと、

```groovy
pipeline({ ->
    // 処理
})
```

です。
groovyのクロージャは``{x, y -> x + y}``のように、``{引数 -> 処理}``のように書きます。引数がない場合は、``引数 ->``の部分は省略可能です[^1]。引数を省略すると、

[^1]: 正確に言うと、``{-> 処理}``と``{ 処理 }``では挙動が変わります。

```groovy
pipeline({
    // 処理
})
```

の形になります。
また、メソッド呼び出しのカッコは、引数がある場合は省略可能なので、

```groovy
pipeline {
    // 処理
}
```

になります。Jenkinsfileと同じ形になりました。

### クロージャ
クロージャとは、メソッドの呼び出し側で動いているような振る舞いをする無名関数です。
こちらの説明が分かりやすいです。
[クロージャ - Apache Groovyチュートリアル](https://koji-k.github.io/groovy-tutorial/closure/index.html)

# 他の言語で書いてみると
Groovyのクロージャを他の言語と対比します。(ここに書ききれない言語につきましてはコメントで補足お願いします)

### Ruby
Groovyの

```groovy
pipeline {
    // 処理
}
```

という記法は、Rubyの

```ruby
pipeline {
  # 処理
}
```

という記法と対応します。見た目は同じですね。GroovyはRubyに強い影響を受けていることがわかります。
Rubyのブロックを、Groovyではクロージャと言います。ブロックとクロージャの違いは、こちらのページが分かりやすいです。
[Rubyist のための他言語探訪 【第 5 回】 Groovy](https://magazine.rubyist.net/articles/0012/0012-Legwork.html)

### JavaScript

Groovyの

```groovy
pipeline {
    // 処理
}
```

という記法は、JavaScriptの

```javascript
pipeline(() => {
    // 処理
})
```

という記法に対応します。JavaScriptではメソッド呼び出しのカッコは省略できず、引数がない場合も引数を囲むカッコを省略できないですが、Groovyでは省略が可能です。



### Java
Groovyの

```groovy
pipeline {
    // 処理
}
```

という記法は、Javaの

```java
pipeline(() -> {
    // 処理
})
```

に対応します。JavaScriptと同様Javaでもメソッド呼び出しのカッコと、引数がない場合は引数を囲むカッコを省略できません。Groovyではこの2つは省略可能です。

### Python
Groovyの

```groovy
pipeline {
    // 処理
}
```

という記法は、Pythonで対応する記法がないのですが、あえて書くとしたら

```javascript
pipeline(lambda: 処理)
```

です。Pythonの場合、lambda式は複数行にまたがることができませんが、Groovyでは可能です。

# 補足

上で、`pipeline`メソッドは

```groovy
def pipeline(Closure cl) {
    // 前処理
    // クロージャを呼び出す
    cl();
    // 後処理
}
```

のようになっていると書きましたが、正確にいうと少し違います。クロージャ内で使用できる関数を制限するため、`delegate`(移譲)を行なっています。`pipeline`メソッドのソースが見つからなかったのですが、より実情に沿ったコードは以下です。

```groovy
def pipeline(@DelegatesTo(PipelineSpec) Closure cl) {
    def pipelineSpec = new PipelineSpec()
    def code = cl.rehydrate(pipelineSpec, this, this)
    code.resolveStrategy = Closure.DELEGATE_ONLY
    code()
}
```

本文中では分かりやすさを優先するため、クロージャをそのまま呼び出す書き方にしています。

# 参考資料
- [クロージャ - Apache Groovyチュートリアル](https://koji-k.github.io/groovy-tutorial/closure/index.html)

# あとがき
Jenkinsfileを作成している最中、「コピペで動いてはいる、ただ、なんで動いているのか分からない」という状態でしたが、Groovyのプログラムということを理解し、実態がつかめました。
Jenkins Pipelineの記事がまだまだ少ないので、分かったことを今後も書けたらと思います。