Jenkinsとshellの初心者が1からジョブを作って勉強になったこと - その２(python script)

1個目のジョブを作成した後に、元々localで動いているpython scriptをJenkinsで動くように新しいジョブを作成し、且つ1個目のジョブと連動する要望があったため、新しジョブの作成及びパイプラインの作成をしました。その中で勉強になったこととかを記載するようにします。

# JenkinsでPythonを使う

## pythonプラグインのインストール

Jenkinsでpythonを使うのに、pythonのプラグインをインストールする必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335050/700ad94f-a69b-5ffe-f576-e307208533a5.png)

すると、Jenkinsジョブの設定に`Execute Python script`の選択肢が出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335050/819f2934-0bf9-9bd6-e592-85e5ce5387ea.png)

ビルド手順を追加して、python scriptを貼り付けば利用できます。

## pythonのインストール

今回使ったサーバーは元々pythonが入っている状態でした。
以下のコマンドを実行すればインストールできると思います。
（dockerの環境でしたので、docker含めたコマンドになります。）
```
docker exec -it --user root [dockerの名前] /bin/bash -c "sudo apt install python3"
```

## python3があっても実行ができなかったトラブルシューティング

ビルド手順に自分の書いたPythonスクリプトを貼り付けたら実行できます。
今はPython3は新しいが、それをインストールしても実行できないこともあります。

`Caused: java.io.IOException: Cannot run program "python" (in directory "（省略）"): error=2, No such file or directory`

調べてみたら、Python3がインストールされていて、`python3 --version`のコマンドは見つかりますが、`python --version`のコマンドが見つからないからエラーになったようです。
以下のように`python-is-python3`というパッケージをインストールしたら解決できました。
```
docker exec -it --user root [dockerの名前] /bin/bash -c "sudo apt install python-is-python3"
```

# Jenkinsのパイプラインを使う

## パイプラインの作成

新規ジョブ作成をクリックして、以下のページでパイプラインを作成すればよいのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335050/bb9d8b01-2a3f-49c0-f252-326123fd07bc.png)

## パイプラインスクリプトの定義

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335050/1bffa68f-d306-bccb-b898-27378a00bbc4.png)

例えば、一番シンプルなケースで、既に2つのジョブを作成していて
2つのジョブを連続で実行したい場合は以下のように書けば実行できます。
パイプラインのステージ、ステップ及びステップの中のジョブを定義します。

```
pipeline {
    agent any
    stages {
        stage("ステージ名１") {
            steps {
                build job: "ジョブ名１"
            }
        }
        stage("ステージ名２") {
            steps {
                build job: "ジョブ名２"
            }
        }
    }
}
```
ジョブ１とジョブ２のところは自分の作ったジョブの名前と一致させる必要があります。

## パラメータ付きビルド

ジョブのビルドのパラメータを設定すれば、ジョブ単体にパラメータを渡すことはできますが、ここはパイプラインでパラメータを渡す例を記載します。（parameters）

```
pipeline {
    agent any
    stages {
        stage("ステージ名１") {
            steps {
                build job: "ジョブ名１",
                parameters: [
                    string(name: "パラメータ名", value: "${パラメータ名}")
                ]
            }
        }
        stage("ステージ名２") {
            steps {
                build job: "ジョブ名２",
                parameters: [
                    string(name: "パラメータ名", value: "${パラメータ名}")
                ]
            }
        }
    }
}

```
# pythonの実装

今回自分の仕事はlocalで動いているpythonスクリプトをJenkinsで動かすだけなので、追加実装はそこまで必要ないが、Jenkinsに移行するに伴って以下の追加対応が必要でした。

## ビルドのパラメータの取得

pythonのosライブラリを使えば取得できます。

```python
import os

param = os.environ['ParamName']
```
一つ要注意なのは、パラメータが空の場合、上記の構文だとパラメータがNoneになり、
後続の処理でこのパラメータを使いたい場合はエラーが出ます。
そのため、以下のように事前に判定したほうが意図しないことを防げます。
```python
import os

if os.getenv('ParamName') is not None:
  param = os.environ['ParamName']
else:
  param = "hoge"
```

## ファイルの中身の取得

今回はジョブ１で取得した結果をジョブ２に渡す必要があり、ジョブ１で結果をファイルに書き込んで、ジョブ２のpython scriptでファイルの中身を読み込んだ仕組みを作りました。
pythonでのファイル読み込みは以下の例になります。

```python
filePath="ファイルのフルパス"
f = open(filePath, 'r')
for line in f:
   print(line)
```

一行しか読み込まない場合はreadlineを使うことができます。
```python
f = open(filePath, 'r')
line = f.readline()
print(line)
```

実際に読み込んだ行の両方に空白文字や改行コードとかが含まれ、それを変数として使うと後続の処理で意図しないことが発生します。そのため、stripを使って、両端にある特定の文字列や空白文字を削除することがお勧めです。
```python
line = f.readline().strip()
```

後、ファイルをそのまま開いた状態にすると、システムのリソース消耗し、他の人もアクセスできなくなりますので、ファイルを閉じることを忘れないでください。
簡単な一文でできます。
```python
f.close()
```

# 最後に

以上は新しいpythonジョブ及びパイプラインを作った時に学んだ知識です。
皆さんのご参考になれれば幸いです。