周回遅れでIT業界デビューしたエンジニアのブログ

就職氷河期にモロにぶち当たり、人生で混迷を極めた末にIT業界に安寧を見出そうとしているアラフォーのお勉強日記です。

JenkinsのBuild PipelineプラグインでCSSを変更する方法

こんにちは。バレンタインデーでしたね。
チョコ山盛り……なこともなく、家に帰ったら何故かたい焼きをもらいました。

f:id:sionff:20180213140850j:plain

Jenkinsでパイプラインを使うとき、便利なプラグインを活用するのも手です。
今回は「Build Pipeline Plugin」でCSSを変更する方法について紹介したいと思います。

お品書き

  • 今回やろうとしたこと
  • 用意するもの
  • Jenkinsの準備をしよう
  • Nginxは意外とスグレモノ
  • Firefox開発ツールでWebページの中身を覗き見
  • いざパイプライン
  • CSSをカスタマイズしよう
  • まとめ

今回やろうとしたこと

Jenkins上で複数のジョブを連続して処理します。このとき、プラグイン「Build Pipeline Plugin」でビューを作成し、このビューで必要な情報のみ表示する、あるいは視認性を向上させるためにCSSを変更します。

用意するもの

  • Jenkins
    • 今回は、連続して処理したいジョブを3つほど用意しました。
  • Nginx
    • CSSファイルの配置に使います。
  • Firefox

Jenkinsの準備をしよう

ジョブを作って、Build Pipeline Pluginプラグインをインストールします。

ジョブを用意する

試しに、連続して処理したいジョブを3つほど作ってみましょう。
f:id:sionff:20180213154921p:plain

ジョブを作ったら、1つめと2つめの設定でビルド後の処理を追加します。対象のプロジェクトはそれぞれ次のジョブです。
f:id:sionff:20180213154941p:plain

実はこの状態で1つめのジョブのビルドを実行するだけでも、3つのジョブが連続して実行されます。でも、どんな状況なのか分かりづらいし、進捗が見やすいならそれに越したことはありません。

そこで、Build Pipeline Pluginの出番となるのですが、CSSも変更しちゃおう! となると多少の準備が必要になります。

Nginxは意外とスグレモノ

「Nginx」って読めますか?
私は最初、読めませんでした。正解は「エンジンエックス」です。
(答えは反転)

NginxはフリーかつオープンソースのWebサーバーで、Apacheと比較されることが多いです。ざっくりとした違いは……

NginxとApache HTTP Serverの違いメモより引用

Apache

マルチプロセスモデル : 接続ごとにプロセスをフォークするのでメモリがいっぱいになりやすい

利点 : メモリ空間が独立しているので、スクリプト言語が組み込みやすい
欠点 : 同時接続が増えるとメモリが欠乏しやすい

→ とは言え、モジュールを使えば、イベント駆動モデルにもできるよ。Nginxには及ばないけど。

Nginx

イベント駆動モデル : 1プロセス内で、接続ごとにイベント処理を行う。1プロセス1CPUなので、CPUの数だけワーカープロセスが作成できる。

利点 : 接続数が増えても、プロセス数やスレッド数が増えない。消費メモリが増えない
欠点 : 1つのメモリ空間で動作するため、スクリプト言語を組み込めないことがある。

→ とは言え、プロキシサーバとして利用すれば、スクリプト言語を呼び出せるよ。

Nginxの主な機能について(WikiPedia

具体的にどの辺りが違うのかは、下の3つのリンクをご覧ください。

Nginxが開発された理由も興味深いです。
news.mynavi.jp

Nginxのインストール

■ダウンロードはこちらから
nginx: download

f:id:sionff:20180213134704p:plain

  1. ダウンロードしたら、好きな場所に展開しましょう。
  2. nginx-x.xx.x(xはバージョン名)フォルダ内のhtmlフォルダに、HTMLやCSSを配置します。(例:hogemoga.html)
  3. exeファイルを起動します。
  4. ブラウザで「http://localhost/hogemoga.html」とアドレスを指定すればWebページが見られます。

f:id:sionff:20180213134054p:plain

実に簡単。個人的には、Apacheでxampp起動して云々かんぬんとかやってるより、はるかにシンプルでいいです。

このNginxは、Build Pipelineの設定の際にCSSファイルを参照するのに使います。(CSSファイル、単にworkspaceに置くのでは認識してくれず、サーバー上にあることが必要なのです。ちょっと面倒)

Nginxのコマンド

Win
起動
start nginx

停止
nginx.exe -s quit

再起動
nginx.exe -s reload
Linux
起動
nginx

停止
nginx -s stop

再起動
nginx -s reload

設定ファイルのチェック
nginx -t

Firefox開発ツールでWebページの中身を覗き見

何のことはないです。
好きなWebページをFirefoxで開いて「F12」ボタンを押すだけです。

1. 画面下部の赤く囲んだ部分に、開発ツールが出てきます。左側にHTML、右側に対応するCSSです。
f:id:sionff:20180213143450p:plain

2. 試しに一部分のCSSのチェックを外すと、チェックが外れたときのレイアウトになります。崩れちゃった!
f:id:sionff:20180213143628p:plain

3. 左端のカーソルボタンを押すとインスペクターが起動して、Web画面でカーソルを動かすと対応する部分の詳細情報が表示されます。ふむふむ。画像のファイル名だってわかっちゃいます。
f:id:sionff:20180213143711p:plain

ここで、nginxにへびさんの画像を置いてみましょう。hebi.pngです。

4. で、さっきのファイル名をnginxに置いた画像に変えると……へびさんが某Yをハイジャックしてしまいました。
f:id:sionff:20180213143830p:plain

結構自由に試すことができます。面白い!

このインスペクター機能を使ってBuild Pipelineで作成したビューのCSSを調べて、自分で用意したCSSで上書きすれば、Build Pipelineを好きなデザインに変更することが出来てしまいます。

いざパイプライン

では、Build Pipelineで進捗が見やすいビューを作りましょう。

Build Pipeline Pluginをインストールする

Jenkinsの管理から、プラグインのインストールを選んで、「Build Pipeline Plugin」をインストールします。
f:id:sionff:20180213155401p:plain

次にViewを追加します。画面上部の「+」ボタンを押します。
f:id:sionff:20180213155635p:plain

ビュー名を入力して、Build Pipeline Viewを選択します。
f:id:sionff:20180213155646p:plain

次に設定をします。とりあえず赤で囲んだ部分を押さえておけば大丈夫。さっきのNginxの設定はココで生きてきます!
f:id:sionff:20180213155654p:plain

全部設定が終わったら、こんな感じでBuild Pipelineのビューが表示されます。
f:id:sionff:20180213155705p:plain

初期状態のままだとお世辞にもオシャレ……とは言いづらいのですが、とりあえず設定できたのでOK! この状態をベースとして、Firefoxセレクタの解析をして、必要に応じてCSSを書けばOKです。

CSSをカスタマイズしよう

あとは、好きなようにCSSをカスタマイズして、nginxのhtmlフォルダに置けば出来上がり!
例えばこんな風。

f:id:sionff:20180213165403p:plain

もはや別モノですね!

まとめ

  • JenkinsのBuild PipelineはCSSを変更できる
  • Nginxは手軽で便利
  • Firefoxの開発ツールはF12ワンキーで起動、画面上でいろいろ試せる

追記

JenkinsはSeleniumと組み合わせたり、スレーブ実行したりも出来ます。こちらはパイプラインでGroovyでコードを書いて制御するバージョンです。Selenium、ノードについても触れていますので参考にどうぞ。
sionff.hatenablog.jp

いま進めている勉強の途中経過をメモしておく

こんにちは。毎日ちょっとずつ勉強を進めているのですが、地道すぎて書くことがなくなりそうなので途中経過をメモしておきたいと思います。

数学

進めているのはこの本です。

もう一度高校数学

もう一度高校数学

やっと6章の微分法に入りました。いよいよ本番!
自分でも遅いなーと思うのですが、何しろ凄い密度の本です……1冊に高校数学のすべてがぎゅーっと詰まっています。特に、4章の関数のボリュームが尋常ではないのでかなり時間を掛けてしまいました。

章立ては下の通りです。統計学の基礎になる部分、機械学習でも頻出する部分が少なくなく、その前段階の基礎的な数学の知識もがっちり網羅しているのでお勧めです。何より解いてて楽しい!!

1章 数とはナニか?
2章 式とはナニか?
3章 方程式・不等式
4章 関数の章
5章 数列および極限
6章 微分
7章 積分
8章 ベクトル
9章 行列
10章 式と証明
11章 集合
12章 論理
13章 場合の数・確率

データサイエンス

いま10章。会社の昼休みに少しずつ進めています。

ゼロからはじめるデータサイエンス ―Pythonで学ぶ基本と実践

ゼロからはじめるデータサイエンス ―Pythonで学ぶ基本と実践

ライブラリなしで、スクラッチから実装を学べるのが気に入って始めて見ました。手を動かしていると、数式がどんな形でプログラムになるのか、どう使われるかがなんとなく分かっていく感じがしていて今のところ楽しいです。cousera「Machine Learning」のOctavePython化するおさらいはこの本で代替してもいいかな、と思いました。

章立てはこんな感じ。

1章 イントロダクション
2章 Python速習コース
3章 データの可視化
4章 線形代数
5章 統計
6章 確率
7章 仮説と推定
8章 勾配下降法
9章 データの取得
10章 データの操作
11章 機械学習
12章 k近傍法
13章 ナイーブベイズ
14章 単純な線形回帰
15章 重回帰分析
16章 ロジスティック回帰
17章 決定木
18章 ニューラルネットワーク
19章 クラスタリング
20章 自然言語処理
21章 ネットワーク分析
22章 リコメンドシステム
23章 データベースとSQL
24章 MapReduce
25章 前進しよう、データサイエンティストとして

機械学習

買いました!電子書籍版が出ているのは知っていたのですが、書籍版になってくれてうれしいです。ありがとうオライリー、ありがとう著者の皆様。

仕事ではじめる機械学習

仕事ではじめる機械学習

本の中でも言及されていますが、coursera「Machine Learning」を修了した人の次のステップとして読むのにふさわしい内容だと思います。「なんでもいいからAIや機械学習で何か改善しろ」ったって、必要がないのにAIや機械学習を使ってもしょうがないし、作ったら作ったで「技術的負債になりやすい」というのは目から鱗でした。ちゃんと考えて導入しないと……じっくり読み進めたいと思います。

英語

IELTSの単語集を、通勤中にヘッドホンしてiPhoneにつないでとりあえず耳に英語を放り込んでいる感じです。

IELTS必須英単語4400

IELTS必須英単語4400

英語についてはIELTSの目標を6.5~7.0に置いていますが、優先度は他よりも下がるので期限を決めずにじっくり進めたいと思います。英語はとにかく習慣化することが大事なので、毎日耳に入れることが最優先。

ちょっと予定より遅れてる

本当はここに統計の勉強を入れたかったのですが……諸々バタバタしていて手が回りそうにないので、遅くとも3月中に英語以外の3冊を終わらせる方向で行きたいと思います。

統計は4月から始まる放送大学の授業を楽しみにしましょうそうしましょう!

「言語処理100ノック2015」をPythonで練習(No.10~19)

f:id:sionff:20180119162019j:plain

www.cl.ecei.tohoku.ac.jp

第2章: UNIXコマンドの基礎(No.10~19)です。
ただし、Pythonのコード部分のみです。UNIXコマンドはやっていません。

以下を出来るだけ意識して書いています。

  • 内包表記を使う
  • 関数化する
  • 一般化する

今回は関数化を結構頑張りました。

そのため他の人の回答よりはコード量が多くなっていますが、全体としては各パートでやることが明確になり、処理も順番こになっているので後々手を入れたりしやすくなっていると思います。

あと、No.16を見て欲しいです。

x行をn分割したとき、余りが出てしまった場合の処理を考える必要があるのですが、qiitaなどでよく見る方法は「行数÷分割数の商を端数切り上げで処理」していますがNGだと思います。

例えば24行のとき、

  • 5分割の場合 > [5,5,5,5,4]になるのでOKに見える
  • 9分割の場合 > [3,3,3,3,3,3,3,3,0]となり9ファイル目に割り当てる行がなくなりNG

なので、端数を適宜割り振ってからリスト化して運用した方がいいと思います。

第2章まえがき

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

import codecs
fname = './input/hightemp.txt'

10. 行数のカウント

行数をカウントせよ.確認にはwcコマンドを用いよ.

with codecs.open(fname, 'r', 'utf_8') as ht:
    print(len([ line for line in ht ]))
        
# 文字コードの問題でファイルが読み込めない場合があるので、
# 今回の設問の場合は、事前にcodecをインポートして対応することにしました。
# 参照:http://osksn2.hep.sci.osaka-u.ac.jp/~taku/osx/python/encoding.html

# 24

11. タブをスペースに置換

タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.

with codecs.open(fname, 'r', 'utf_8') as ht:
    for line in ht:
        ln = line.replace('\t', ' ')
        print(ln)

# 高知県 江川崎 41 2013-08-12
# 埼玉県 熊谷 40.9 2007-08-16
# 岐阜県 多治見 40.9 2007-08-16
# ...

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.

def doc_read():
    """
    必要なカラムを抽出する処理。
    """
    with codecs.open(fname, 'r', 'utf_8') as ht:
        lines = [ line.strip('\n').split('\t') for line in ht ]
    return lines

def create_col(path, doc, num):
    """
    抽出したカラムをファイルに書き出す処理。
    """
    data = [ln[num] for ln in doc]
    doc_col = "\n".join(data)
    with codecs.open(path, 'w', 'utf_8') as cd:
        cd.write(doc_col)

create_col('./output/col1.txt', doc_read(), 0)
create_col('./output/col2.txt', doc_read(), 1)

# col1.txt
# 高知県
# 埼玉県
# 岐阜県
# ...

# col2.txt
# 江川崎
# 熊谷
# 多治見
# ...

13. col1.txtとcol2.txtをマージ

12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.

def doc_read(path):
    """
    ドキュメントを読み込む処理。
    """
    cols = []
    for pt in path:
        with codecs.open(pt, 'r', 'utf_8') as ht:
            lines = [ line.strip('\n') for line in ht ]
        cols.append(lines)
    return merge_col(cols)

def merge_col(cols):
    """
    ドキュメントをマージする処理。
    """
    length = len(cols[0])
    for i in range(len(cols)):
        doc_lines = [ cols[0][i] + '\t' + cols[1][i] for i in range(length) ]
        doc = "\n".join(doc_lines)
    return create_txt(doc)

def create_txt(doc):
    """
    ドキュメントを書き出す処理。
    """
    with codecs.open('./output/merge.txt', 'w', 'utf_8') as cd:
        cd.write(doc)
    return None

read_path = ['./output/col1.txt', './output/col2.txt']
    
doc_read(read_path)

# merge.txt
# 高知県	江川崎
# 埼玉県	熊谷
# 岐阜県	多治見
# ...

# ここはあんまり上手く書けなかった……後日リベンジするかも。

14. 先頭からN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.

import re

def input_num():
    """
    数字を入力する処理。自然数以外の場合は再度入力を受け付ける。
    """
    while 1:
        num = input()
        if re.match('^[0-9]+$', num) and int(num) > 0:
            break
        else:
            continue
    return output_lines((int(num)), doc_read())

def doc_read():
    """
    ドキュメントを読み込む処理。
    """
    with codecs.open(fname, 'r', 'utf_8') as ht:
        lines = [ line for line in ht ]
    return lines

def output_lines(num, lines):
    """
    先頭からN行を出力する処理。
    """
    i = 0
    for line in lines:
        print("".join(line))
        i = i + 1
        if i >= num:
            break
        else:
            continue
                
input_num()

# 「3」を入力

# 高知県	江川崎	41	2013-08-12
# 埼玉県	熊谷	40.9	2007-08-16
# 岐阜県	多治見	40.9	2007-08-16

15. 末尾のN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.

import re

def input_num():
    """
    数字を入力する処理。自然数以外の場合は再度入力を受け付ける。
    """
    while 1:
        num = input()
        if re.match('^[0-9]+$', num) and int(num) > 0:
            break
        else:
            continue
    return output_lines(int(num), doc_reverse())

def doc_reverse():
    """
    ドキュメントを読み込む処理。末尾なので逆順を返すようにする。#14と見比べてみて下さい。
    """
    with codecs.open(fname, 'r', 'utf_8') as ht:
        lines = [ line for line in ht ]
    return lines[::-1]

def output_lines(num, lines):
    """
    先頭からN行を出力する処理。
    """
    i = 0
    for line in lines:
        print("".join(line))
        i = i + 1
        if i >= num:
            break
        else:
            continue

input_num()

# 「3」を入力

# 愛知県	名古屋	39.9	1942-08-02
# 山形県	鶴岡	39.9	1978-08-03
# 山梨県	大月	39.9	1990-07-19

16. ファイルをN分割する

自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ.

import re

def input_num():
    """
    数字を入力する処理。自然数以外の場合は再度入力を受け付ける。
    """
    while 1:
        num = input()
        if re.match('^[0-9]+$', num) and int(num) > 0:
            break
        else:
            continue
    return doc_read(int(num))

def doc_read(num):
    """
    ドキュメントを読み込む処理。
    """
    with codecs.open(fname, 'r', 'utf_8') as ht:
        lines = [ line for line in ht ]
    return part_num(lines, len(lines), num)

def part_num(lines, length, num):
    """
    行数をできるだけ均等になるようにN分割しリストを返す処理。
    """
    split_num = [ (length + i) // num for i in range(num) ]
    return split_file(lines, list(reversed(split_num)))

# 整数をできるだけ均等になるようにN分割
# 参考:https://qiita.com/keisuke-nakata/items/c18cda4ded06d3159109

# リストの逆順を取得する
# 参考:https://www.python-izm.com/advanced/reverse/

def split_file(lines, numbers):
    """
    ドキュメントをN分割し、つどファイルを出力する処理。
    """
    n = 0
    counter = 0
    for num in numbers:
        ln = []
        for i in range(n, num+n):
            ln.append(lines[i])
        with codecs.open("./output/file_split{}.txt".format(counter), 'w', 'utf_8') as fs:
            fs.write("".join(ln))
        n = n + num
        counter = counter + 1

input_num()

# 「9」を入力
# file_split0.txt
# file_split1.txt
# file_split2.txt
# file_split3.txt
# file_split4.txt
# file_split5.txt
# file_split6.txt
# file_split7.txt
# file_split8.txt

# x行をn分割したとき、余りが出てしまった場合の処理を考える必要があります。
# qiitaでよく見る方法は行数÷分割数の商を端数切り上げで処理していますが、これはNGだと思います。
# 例えば本問のように24行のとき……
# 5分割の場合 > [5,5,5,5,4]になるのでOKに見える
# 9分割の場合 > [3,3,3,3,3,3,3,3,0]となり9ファイル目に割り当てる行がなくなりNG
# なので、端数を適宜割り振ってからリスト化して運用した方がいいと思います。

17. 1列目の文字列の異なり

1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.

# 内包表記で回す場合
with codecs.open(fname, 'r', 'utf_8') as ht:
    print(set([ line.split('\t')[0] for line in ht ]))

# for文で回す場合
#lines = []
#with codecs.open(fname, 'r', 'utf_8') as ht:
#    for line in ht:
#        lines.append(line.split('\t')[0])
#
#print(set(lines))

# 可読性がわりと死にますが内包表記だと1行で済みます。

# {'和歌山県', '岐阜県', '山形県', '埼玉県', '千葉県', '愛知県', '群馬県', '山梨県', '大阪府', '静岡県', '愛媛県', '高知県'}

18. 各行を3コラム目の数値の降順にソート

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

# ラムダ式
with codecs.open(fname, 'r', 'utf_8') as ht:
    lines = ht.readlines()
    lines.sort(key=lambda line: float(line.split('\t')[2]), reverse=True)

for line in lines:
    print(line)

# 高知県	江川崎	41	2013-08-12
# 埼玉県	熊谷	40.9	2007-08-16
# 岐阜県	多治見	40.9	2007-08-16
# ...

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.

def read_file():
    """
    ドキュメントを読み込んで、1列目だけを抽出する処理。
    """
    with codecs.open(fname, 'r', 'utf_8') as ht:
        lines = [ line.split('\t')[0] for line in ht ]
    return create_dict(lines)

def create_dict(lines):
    """
    dict型に変換しつつ、1列目の文字列の出現頻度を求める処理。
    """
    words = {}
    for line in lines:
        if line in words:
            words[line] += 1
        else:
            words[line] = 1
    
    return sort_dict(words)

def sort_dict(words):
    """
    出現頻度を降順にソートする処理。
    """
    words_sorted = sorted(words.items(), key=lambda x: x[1], reverse=True)
    return output(words_sorted)

def output(words):
    """
    得られた結果を出力する処理。
    """
    for entry in words:
        print(entry)
    return None
        
read_file()

# ('埼玉県', 3)
# ('山形県', 3)
# ('山梨県', 3)
# ('群馬県', 3)
# ('岐阜県', 2)
# ('静岡県', 2)
# ('愛知県', 2)
# ('千葉県', 2)
# ('高知県', 1)
# ('和歌山県', 1)
# ('愛媛県', 1)
# ('大阪府', 1)