Jenkins Pipeline メモ

Jenkins Pipeline、Jenkinsfileではまったところメモ

## `parallel`と`node`を組み合わせた場合のワークスペースの確保方法の違い

* Jenkins 2.63
* Pipeline: Nodes and Processes 2.11

ラベル指定なしの`node`の場合、`env.WORKSPACE`は別々のものが確保される

```groovy:Jenkinsfile
def allocateNode（） {
    node {
        echo env.WORKSPACE
    }
}

parallel a: { allocateNode() }, b: { allocateNode() }
```

```text:実行ログ
[Pipeline] parallel
[Pipeline] [a] { (Branch: a)
[Pipeline] [b] { (Branch: b)
[Pipeline] [a] node
[Pipeline] [b] node
[a] Running on master in /var/lib/jenkins/jobs/pipeline-test/workspace
[Pipeline] [a] {
[b] Running on master in /var/lib/jenkins/jobs/pipeline-test/workspace@2
[Pipeline] [a] echo
[a] /var/lib/jenkins/jobs/pipeline-test/workspace
[Pipeline] [a] }
[Pipeline] [b] {
[Pipeline] [a] // node
[Pipeline] [a] }
[Pipeline] [b] echo
[b] /var/lib/jenkins/jobs/pipeline-test/workspace@2
[Pipeline] [b] }
[Pipeline] [b] // node
[Pipeline] [b] }
[Pipeline] // parallel
[Pipeline] End of Pipeline
Finished: SUCCESS
```

ラベル指定有りの`node`の場合、`env.WORKSPACE`は同じものが利用される

```groovy:Jenkinsfile
def allocateNode（） {
    node('docker') {
        echo env.WORKSPACE
    }
}

parallel a: { allocateNode() }, b: { allocateNode() }
```

```text:実行ログ
[Pipeline] parallel
[Pipeline] [a] { (Branch: a)
[Pipeline] [b] { (Branch: b)
[Pipeline] [a] node
[a] Running on jenkins-slave-docker1 in /home/jenkins-slave/workspace/pipeline-test
[Pipeline] [b] node
[Pipeline] [a] {
[Pipeline] [a] echo
[a] /home/jenkins-slave/workspace/pipeline-test
[Pipeline] [a] }
[b] Running on jenkins-slave-docker1 in /home/jenkins-slave/workspace/pipeline-test
[Pipeline] [a] // node
[Pipeline] [b] {
[Pipeline] [a] }
[Pipeline] [b] echo
[b] /home/jenkins-slave/workspace/pipeline-test
[Pipeline] [b] }
[Pipeline] [b] // node
[Pipeline] [b] }
[Pipeline] // parallel
[Pipeline] End of Pipeline
Finished: SUCCESS
```

`ws`を利用すればワークスペースを確保することができる。

```groovy:Jenkinsfile
def allocateNode(buildType) {
    node('docker') {
        ws("${env.WORKSPACE}/${buildType}") {
            echo env.WORKSPACE
        }
    }
}

parallel a: { allocateNode('a') }, b: { allocateNode('b') }
```

```text:実行ログ
[Pipeline] parallel
[Pipeline] [a] { (Branch: a)
[Pipeline] [b] { (Branch: b)
[Pipeline] [a] node
[Pipeline] [b] node
[a] Running on jenkins-slave-docker1 in /home/jenkins-slave/workspace/pipeline-test
[Pipeline] [a] {
[Pipeline] [a] ws
[a] Running in /home/jenkins-slave/workspace/pipeline-test/a
[Pipeline] [a] {
[Pipeline] [a] echo
[a] /home/jenkins-slave/workspace/pipeline-test/a
[Pipeline] [a] }
[Pipeline] [a] // ws
[Pipeline] [a] }
[Pipeline] [a] // node
[Pipeline] [a] }
[b] Running on jenkins-slave-docker1 in /home/jenkins-slave/workspace/pipeline-test
[Pipeline] [b] {
[Pipeline] [b] ws
[b] Running in /home/jenkins-slave/workspace/pipeline-test/b
[Pipeline] [b] {
[Pipeline] [b] echo
[b] /home/jenkins-slave/workspace/pipeline-test/b
[Pipeline] [b] }
[Pipeline] [b] // ws
[Pipeline] [b] }
[Pipeline] [b] // node
[Pipeline] [b] }
[Pipeline] // parallel
[Pipeline] End of Pipeline
Finished: SUCCESS
```

## 複数のリポジトリをチェックアウト

`dir` で囲もう

```groovy:Jenkinsfile
node {
    git url: 'repo.git'
    checkout scm
    dir('subrepo') {
        git url: 'subrepo.git'
    }
}
```

* https://stackoverflow.com/questions/37395860/jenkinsfile-with-two-git-repositories

## `docker` + `dir` + `sh` の動き

今の所うまく動かない。

* https://issues.jenkins-ci.org/browse/JENKINS-33510

```groovy:Jenkinsfile
node('docker') {
    echo env.WORKSPACE
    sh 'mkdir -p hoge/hoge'
    sh 'pwd'
    dir('hoge/hoge') {
        sh 'pwd' // これはOK
    }
    sh 'pwd'

    docker.image('ruby:2.4.1').inside {
        sh 'pwd'
        dir('hoge/hoge') {
            sh 'pwd' // こっちはダメ
        }
        sh 'pwd'
    }
}
```

```text:実行ログ
[Pipeline] node
Running on jenkins-slave-docker1 in /home/jenkins-slave/workspace/pipeline-test
[Pipeline] {
[Pipeline] echo
/home/jenkins-slave/workspace/pipeline-test
[Pipeline] sh
[pipeline-test] Running shell script
+ mkdir -p hoge/hoge
[Pipeline] sh
[pipeline-test] Running shell script
+ pwd
/home/jenkins-slave/workspace/pipeline-test
[Pipeline] dir
Running in /home/jenkins-slave/workspace/pipeline-test/hoge/hoge
[Pipeline] {
[Pipeline] sh
[hoge] Running shell script
+ pwd
/home/jenkins-slave/workspace/pipeline-test/hoge/hoge
[Pipeline] }
[Pipeline] // dir
[Pipeline] sh
[pipeline-test] Running shell script
+ pwd
/home/jenkins-slave/workspace/pipeline-test
[Pipeline] sh
[pipeline-test] Running shell script
+ docker inspect -f . ruby:2.4.1
.
[Pipeline] withDockerContainer
jenkins-slave-docker1 does not seem to be running inside a container
$ docker run -t -d -u 1001:1001 -w /home/jenkins-slave/workspace/pipeline-test -v /home/jenkins-slave/workspace/pipeline-test:/home/jenkins-slave/workspace/pipeline-test:rw -v /home/jenkins-slave/workspace/pipeline-test@tmp:/home/jenkins-slave/workspace/pipeline-test@tmp:rw -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** --entrypoint cat ruby:2.4.1
[Pipeline] {
[Pipeline] sh
[pipeline-test] Running shell script
+ pwd
/home/jenkins-slave/workspace/pipeline-test
[Pipeline] dir
Running in /home/jenkins-slave/workspace/pipeline-test/hoge/hoge
[Pipeline] {
[Pipeline] sh
[hoge] Running shell script
JENKINS-33510: working directory will be /home/jenkins-slave/workspace/pipeline-test not /home/jenkins-slave/workspace/pipeline-test/hoge/hoge
+ pwd
/home/jenkins-slave/workspace/pipeline-test
[Pipeline] }
[Pipeline] // dir
[Pipeline] sh
[pipeline-test] Running shell script
+ pwd
/home/jenkins-slave/workspace/pipeline-test
[Pipeline] }
$ docker stop --time=1 b10d417e8ef04951965997a6d80c4357eec39758e634f833b1d2cc5704cd223a
$ docker rm -f b10d417e8ef04951965997a6d80c4357eec39758e634f833b1d2cc5704cd223a
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
```

## dockerにログインしてるユーザー

jenkins（かjenkins-slave）を実行しているユーザー。ユーザー名ではなくuid、gidを使っている。

- https://github.com/jenkinsci/docker-workflow-plugin/blob/master/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java#L304


## 環境変数

`-u $(id -u)`の有無で`＄HOME`が異なる。

```
% docker run -it node:6 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d57d403199c8
TERM=xterm
NODE_VERSION=6.13.0
YARN_VERSION=1.3.2
HOME=/root

% docker run -it -u $(id -u) node:6 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=f85550e1e0a8
TERM=xterm
NODE_VERSION=6.13.0
YARN_VERSION=1.3.2
HOME=/

% docker run -it -u $(id -u) ruby:2.4.3 env
PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=b5771e49dde3
TERM=xterm
RUBY_MAJOR=2.4
RUBY_VERSION=2.4.3
RUBY_DOWNLOAD_SHA256=23677d40bf3b7621ba64593c978df40b1e026d8653c74a0599f0ead78ed92b51
RUBYGEMS_VERSION=2.7.6
BUNDLER_VERSION=1.16.1
GEM_HOME=/usr/local/bundle
BUNDLE_PATH=/usr/local/bundle
BUNDLE_BIN=/usr/local/bundle/bin
BUNDLE_SILENCE_ROOT_WARNING=1
BUNDLE_APP_CONFIG=/usr/local/bundle
HOME=/
```

Jenkinsで起動するときは常に-uが設定されるので上書きしない限り`HOME=/`になっていることを前提としたほうがいい。

`LANG`と`TZ`は次を指定しておきたい。

```
-e LANG=C.UTF-8 -e TZ=Asia/Tokyo
```

## `HOME=/`と書き込み権限

### ruby(というかbundler)

`HOME`は使っていないし、`GEM_HOME`、`BUNDLE_BIN`のパーミッションを`777`にしている。偉大な正義。

- https://github.com/docker-library/ruby/blob/master/2.4/jessie/Dockerfile#L65

### `yarn`とか`bower`

こんかな感じで警告が出る

```
% docker run -it -v (pwd):/opt/project --workdir /opt/project -u (id -u) node:6 yarn
yarn install v1.3.2
warning Skipping preferred cache folder "/.cache/yarn" because it is not writable.
warning Selected the next writable cache folder in the list, will be "/tmp/.yarn-cache-501".
[1/4] Resolving packages...
[2/4] Fetching packages...
...
warning Your current version of Yarn is out of date. The latest version is "1.5.1" while you're on "1.3.2".
info To upgrade, run the following command:
$ curl -o- -L https://yarnpkg.com/install.sh | bash
Done in 70.84s.
Error: EACCES: permission denied, open '/.yarnrc'
    at Error (native)
```

`$HOME/.cache/yarn` = `/.cache/yarn`だけどどこの馬の骨とも知らない`-u $(id -u)`なユーザは当然書き込み権限などない。`-e YARN_CACHE_FOLDER=/tmp`のように指定して回避する方法もある。

`/.yarnrc`（のlastUpdateCheckの更新）についてはちょっといい案が浮かばない。書き込み権限のある適当なHOMEを設定するのが一番かな。。

## 参考にしているJenkinsfile

* https://github.com/jenkinsci/jenkins/blob/master/Jenkinsfile
* https://github.com/jenkinsci/pipeline-examples
* https://jenkins.io/blog/2016/08/10/rails-cd-with-pipeline/
* https://github.com/jenkins-infra/jenkins-infra/blob/staging/Jenkinsfile
* https://github.com/jenkins-infra/jenkins.io/blob/master/Jenkinsfile
* https://github.com/jenkins-infra/pipeline-library/blob/master/vars/buildPlugin.groovy