OmniAICreator/Qiita-1.07M huggingface dataset:
Jenkins Scripted Pipeline での parameters の書き方

# はじめに

Jenkins Pipeline には `node` で始まる Scripted Pipeline と `pipeline` で始まる Declarative Pipeline の2種類があります。
Scripted Pipeline で parameters の choice を書こうとしたときにはまったので備忘録です。

# parameters

## Declarative Pipeline での書き方

[Pipeline Syntax](https://jenkins.io/doc/book/pipeline/syntax/#parameters) （公式ドキュメント）

```groovy:Jenkinsfile
pipeline {
    agent any
    parameters {
        choice(name: 'CHOICES', choices: ['one', 'two', 'three'], description: '')
    }
    stages {
        stage('echo parameters') {
            steps {
                echo "CHOICES: ${params.CHOICES}"
            }
        }
    }
}
```

## Scripted Pipeline での書き方

[Is it possible to make a parameterized scripted pipeline in Jenkins by listing the parameters in actual script, not in job config - Stack Overflow](https://stackoverflow.com/questions/53747772/is-it-possible-to-make-a-parameterized-scripted-pipeline-in-jenkins-by-listing-t)

```groovy
node {
    properties([
        parameters([
            string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: ''),
            booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '')
        ])
    ])
    ....
}
```

choice に関する記述はありません。

# parameters: choice

## Scripted Pipeline でうまくいかない書き方

```groovy:Jenkinsfile

node {
    properties([
        parameters([
            choice(name: 'CHOICES', choices: ['one', 'two', 'three'], description: ''),
        ])
    ])
    stage('echo parameters') {
        echo "CHOICES: ${params.CHOICES}"
    }
}
```

と書いたところ以下のエラーが発生しました。

```groovy
java.lang.IllegalArgumentException: Could not instantiate {name=CHOICES, choices=[one, two, three], description=}
for ChoiceParameterDefinition(name: String, choices: String, description: String):
java.lang.ClassCastException: hudson.model.ChoiceParameterDefinition.choices expects
class java.lang.String but received class java.util.ArrayList
```

Jenkins のバージョンは `ver. 2.32.1` とやや古めです。

## Scripted Pipeline で動く書き方

[Jenkinsfileの書き方 (Jenkins Pipeline) - Qiita](https://qiita.com/zizu21105/items/38831adf42e3cfeed66a#3-%E3%83%93%E3%83%AB%E3%83%89%E6%99%82%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E5%85%A5%E5%8A%9B) を参考にしました。

>公式ドキュメントではchoiceの場合、choices: ['one', 'two', 'three']
のようにかけるそうですが、なぜか私の環境ではsyntax errorがでてしまったため、
以下のように\n改行コードを入れることでセレクトボックス入力が可能になりました。

```Groovy:Jenkinsfile
node {
    properties([
        parameters([
            choice(name: 'CHOICES', choices: 'one\ntwo\nthree', description: ''),
        ])
    ])
    stage('echo parameters') {
        echo "CHOICES: ${params.CHOICES}"
    }
}

```

何が原因かは調査していません。

# おわりに

現在は Declarative Pipeline が主流だとは思うのですが、同じ Jenkins Pipeline の中に2種類あるので検索しても情報がごちゃ混ぜで欲しい情報に辿り着くのが難しいですね。
Jenkins 自体レガシーになりつつあると思うので早めに GitHub Actions に移行していきたいところです。 GitHub Enterprise にも今年実装予定のようなので期待しています。
セッションとクッキーの違い

##前提知識（HTTPプロトコルについて）
###HTTPプロトコルとは
HTTPで通信するルールのこと。
HTTPプロトコルは状態を持たない。
つまり、HTTPプロトコルではリクエストを送り、レスポンスを受け取った時点で、その通信は終了してしまいます。

これでは分かりにくいので、具体例を挙げると、
Googleで「犬　可愛い」と検索をする。
そしてもう一度、Googleで、「犬　可愛い」と検索をする。

この2回はリクエストとレスポンスが全く同じことをしていますが、
別の通信だと認識されてしまいます。

これが、HTTPプロトコルは状態を持たないことを意味しています。
(ステートレスプロトコルと言います）

###アプリケーションでの具体例

上記のことの問題点として、アプリケーションの機能を例に挙げて説明します。
例えば、ログイン画面でログイン後、次のページへ遷移するとユーザー情報を
引き継げないなどの問題が挙げられます。

これらを解決するのがクッキーとセッションです。
 

#Cookie（クッキー）

クッキーとは、HTTPヘッダーに格納できるテキストデータのことです。
WebサーバーからWebブラウザへHTTPレスポンスのヘッダを利用して情報を送ります。
この情報は「名前＝値」の組み合わせで表されます。
そして、このデータはクライアントのブラウザに保存されます。

（より視覚的にクッキーの状態などを確認する方法としてInetSpyなどのサービスもありますので、興味のある方は調べてみてください）

このcookieを利用すればブラウザに値が格納されているため、
先ほどのログイン問題でも発生したHTTPプロトコルの値を保持できないという問題は解決できます。

しかし、別の問題が発生します。

###セキュリティ上の問題 

クッキーにはセキュリティ上の問題があるのです。
なぜなら、クッキーはクライアント側に値を保存するからです。

クライアント側にあるということはクライアント側で変更が可能ということもあり、
*少し知識のある人なら誰でも簡単見ることができます。*
例えば先ほど紹介したInetSpyなど

そこでようやく、登場するのがセッションです。

#Session（セッション）

クライアントとサーバーの通信状態をセッションといいます。

セッションにはIDを割り振って管理します。
保持されたセッションが欲しいときはIDを指定して取り出せます！

クッキーと違って、セッションを使用すれば、値はサーバー側に保存されてるためクライアント側からは操作できません。

では、どうやってセッションIDをサーバーに送るかというと、
クッキーに、このセッションIDを格納します。

そうすることで、クッキーの問題点であった中身を見られても問題ありません。
なぜなら、セッションIDを見たとしても、関連している中身はサーバーにあるためデータを改ざんすることはできないからです。

#まとめ

###cookie

保存場所：クライアント側
安全性：データ改ざんの可能性が高い

###session

保存場所：サーバー側
安全性：データ改ざんの可能性は低い

取り出すにはセッションID をサーバーに送信する必要あり
セッションIDにクッキーを格納してデータ送信を安全に実現する
fallthroughはwhere条件を無視するので非排他的条件には使えなさそう

## 概要

Swiftのswitch文では、通常caseまたはdefaultを1つしか通りません。
しかし以降のcaseも続けて実行したい場合には、 **fallthroughを使うことで実現することが可能です :checkered_flag:**
これは **判定値がcaseに当てはまらなかったとしても実行されます。**

```swift
func testFallthrough(x: Int) {
  switch (x) {
    case 0:
      print("zero")
      fallthrough
    case 1: // x != 1 だがfallthroughしているので実行される
      print("one")
      fallthrough
    default: // x != default だがfallthroughしているので実行される
      print("non-binary")
  }
}

print(testFallthrough(x: 0)) // "zero" "one" "non-binary" が出力される
```

## やりたかったこと

これを利用し、 **ある2次元の座標点Pが、事前に定義した各領域に含まれるかどうかを判定するため、switch文で当たり判定の算出を試みました :triangular_ruler:**
例として、2つの領域 `rightAbove` と `leftBelow` を定義し、その中にPが含まれている場合にその情報を配列で返したいとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17214/d60d2a7c-548b-b542-c3ea-6bc38a308f8a.png)

座標が領域に含まれることをチェックするcase文では、 **whereにより判定をかけています。**

ここで自分は**「fallthroughはcaseにある条件を無視するのであってwhere句の条件は無視されない」**と勝手に思い込んで、以下のような実装をしてしまいました。

```swift
enum AreaType: String {
    case rightAbove = "右上"
    case leftBelow = "左下"
}

func checkHitAreas(x: Int, y: Int) -> [AreaType] {
    var hitAreas: [AreaType] = []
    switch (x, y) {
        case let (x, y) where -5...10 ~= x && -5...10 ~= y:
            hitAreas.append(.rightAbove)
            fallthrough
        case let (x, y) where -10...5 ~= x && -10...5 ~= y:
            hitAreas.append(.leftBelow)
            fallthrough
        default:
            break
    }
    return hitAreas
}
```

しかし実行してみると、**見事にwhere句の条件も無視されていました。**

```swift
print(checkHitAreas(x:  0, y:  0).map { $0.rawValue }) // 期待: [右上, 左下] -> 実際: [右上, 左下]
print(checkHitAreas(x:  6, y:  6).map { $0.rawValue }) // 期待: [右上] -> 実際: [右上, 左下]
print(checkHitAreas(x: -6, y: -6).map { $0.rawValue }) // 期待: [左下] -> 実際: [右上, 左下]
```

**fallthroughはcaseの指定だけでなくwhereの条件も全て無視するんですね :warning:**
わざわざwhereが書いてると、直感的には判定に使ってくれそうな気がしていて勘違いしていました :droplet: 

## 正しいコード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17214/056fbae4-f4c6-28ec-1ab9-3b34d8acc649.png)

**今回のように排他的ではない条件で判定をしたい場合は、素直にif文で書く他ないようです :relieved:**

```swift
func checkHitAreas(x: Int, y: Int) -> [AreaType] {
    var hitAreas: [AreaType] = []
    if -5...10 ~= x && -5...10 ~= y {
      hitAreas.append(.rightAbove)
    }
    if -10...5 ~= x && -10...5 ~= y {
      hitAreas.append(.leftBelow)
    }
    return hitAreas
}

print(checkHitAreas(x:  0, y:  0).map { $0.rawValue }) // [右上, 左下]
print(checkHitAreas(x:  6, y:  6).map { $0.rawValue }) // [右上]
print(checkHitAreas(x: -6, y: -6).map { $0.rawValue }) // [左下]
```

## 参考リンク

- [Control Flow — The Swift Programming Language \(Swift 5\.1\)](https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html)
- [【Swift】switch文を使いこなす \- Qiita](https://qiita.com/shimesaba/items/55bfe911c2f86055cbe1)
- [Swiftのfallthroughはcaseの条件を評価してくれない \- LS1qJの日記](http://ls1qj.hatenablog.com/entry/2014/06/08/222030)
Q5.特定の条件に合致するセルの情報を取得するには？

例えば、以下のようなデータがあります。
便宜上ここでは、ある職場のメンバーの年齢が書いてある情報と仮定しておきます。


![Q5-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560479/0483df95-220b-4172-ddc6-86b0dd50dc57.png)


ここから、年齢が30歳以上の人が何人いるか調べるというマクロを作ってみます。
調べた後に、ウィンドウで30歳以上の人は〇〇人です。といった具体にメッセージを表示させてみます。

ポイントは、条件分岐です。if文を使って、30以上の人が何人いるかを
どのように表現していくかです。
また、どのようにセルの情報を参照していくかも一つのポイントです。
今回は、forEach文という構文を使用しています。

それでは、マクロの方を見ていきましょう。



![Q5-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560479/483dba85-66ae-2daa-9517-bfb269bca789.png)

```VB
Sub Q5_Answer()
    Dim i As Range
    Dim cnt As Integer
    For Each i In Range("C2:C8")
        If i.Value >= 30 Then
            cnt = cnt + 1
        End If
    Next i
    MsgBox "30歳以上の人数は" & cnt & "人です。"
    
End Sub
```



3行目にポイントとなるForEach文が出てきていますね。
ForEach文は複数のデータを扱うときに便利な構文です。

例えば、今回の例でいうと、Range("C2:C8")としているので、
iには、C2～C8のRangeの情報が順番に格納されていきます。

forEach文のループ内に、30以上のデータを調べるif文を記述し、
条件に合致したら、cntという人数をカウントする変数に、値を足していきます。


そして、最後MsgBoxという関数で、メッセージを表示させています。



ちなみに、マクロを実行した結果は以下のような画面となります。

![Q5-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/560479/e295966d-6f36-100f-30cb-1f4475fc5cc2.png)


ちゃんと、人数が表示されていますね。
もちろん、データを変えれば結果も変わりますので、試してみてください。


今回はかなり簡易なマクロではありますが、いろいろ応用ができます。
例えば、アンケートに"はい"と答えた人の数をカウントするマクロだったりとか、
飲み会の出欠をカウントする時だったりとか特定のデータを調べるというシーンは、
日常的に結構あったりします。

そういった場合にマクロに落とし込むことができればかなり楽になるでしょう。
RDKit　簡単インストール

まずは、アナコンダにRDKitをインストールして
ケモインフォマティクスを始めましょう。

![a05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237930/f8dbcc16-49a9-50eb-399c-0984e110a6de.png)

１、アナコンダを起動して、
　　仮想環境を作り、
　　ターミナルを開く。
２、rdkitをインストール
　　conda install -c rdkit rdkit
３、必要なライブラリも入れましょう。
　　Jupyter、scikit-learn　など

簡単ですね。
次は、Google Colaboratory　にRDKitをインストールして
ケモインフォマティクスを始めましょう。

![a04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237930/98a8c1c4-0102-aa28-e5bc-c8eb30653ae0.png)


こちらも、アナコンダの場合と
基本的には同じですが、注意点が２点あります。

１、condaがインストールされていない。
２、pythonの環境が２つ存在している。
　　conda　環境：追加インストールされる場所。
　　Colaboratory　環境：pathを通して利用する。
３、まずはインストールです。
　　%%bash
　　wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
　　chmod +x Miniconda3-latest-Linux-x86_64.sh
　　./Miniconda3-latest-Linux-x86_64.sh -b -f -p /usr/local
　　conda install -c rdkit rdkit
４、pathを通します。
　　import sys
　　sys.path.append('/usr/local/lib/python3.7/site-packages/')
５、必要なライブラリーは　全部入ってます。

簡単ですね。
さあ、ケモインフォマティクスを始めましょう。

－－以上－－

![a06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237930/2b95ef62-1c79-ce80-108b-24efafd2be35.png)

![a07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237930/67304b02-5173-6e5c-33bd-ac9aa969b9a2.png)
EC2+Django+nginxを本番環境にあげたときの備忘録

# 最初に
ec2(linux)にdjango+nginxデプロイした時の備忘録です。
導入前は、本番環境にもかかわらず、runserver_plusを使用していました。
（個人開発的なものなので良しとしていた）
公式にもrunserverは本番環境で使うな！と書いてあります:sweat_smile:
>runserver
>DO NOT USE THIS SERVER IN A PRODUCTION SETTING
>https://docs.djangoproject.com/ja/3.0/ref/django-admin/

# 導入
以下記事を参考に導入しました。
https://qiita.com/pokotsun/items/1272479e36c5146c6609

# エラー確認方法と対処
以下ファイルを見る。
`/var/log/nginx/error.log`

### エラーがある
ググっていけばOK。

### エラーがない
EC2のセキュリティグループを疑ってみる。
そもそもリクエストがEC2に届いていない可能性あり。
インバウンドのHTTP,HTTPSの設定が自宅のIPになっていて接続できないことがありました。


### httpで接続できるのに、httpsで接続できない！
Amazonでのセキュリティグループの設定を忘れていました。。
EC2に紐づくセキュリティグループに、TYPE"HTTPS"の設定を追加することで解消。


# 躓いたとこ
### cssが適用されない！
>https://qiita.com/xKxAxKx/items/f43d1bddbc4fb31013b1

権限がないことが原因でした。
以下で解決しました。
・`chmod 755 (プロジェクトまでのパス)/static/`
・`sudo chmod o+x /home/ec2-user/`で解決
GitLabCEをS3にバックアップする時のディレクトリを DIRECTORY= で指定する

複数のGitLabCEのバックアップを AWS S3 に保管する場合、サブディレクトリを切って、GitLabCEごとに置きたくなりますが、公式ドキュメントだと分かりづらかったのでやり方をメモします。

環境は以下の通りです。

* GitLabCE (12.1.6)
* Amazon Linux (ver1)
* yumインストール

方法は簡単で、バックアップコマンドに `DIRECTORY=<S3のPath>` を追加で指定します。

```bash
gitlab:backup:create DIRECTORY=onpre-gitlab01
```

cronでやるなら以下の様に設定するだけです。

```cron
# Backup gitlab weekly
30 15 * * SUN root gitlab-rake gitlab:backup:create DIRECTORY=onpre-gitlab01
```

なお、ドキュメントを読んで気づきましたが、12.2以降は以下の `gitlab-backup` コマンドが推奨される様です。私の環境はギリギリ12.1だったため、従来の gitlab-rake を実行しています。

```bash
0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1
```

## 補足情報

GitLabCEにはS3にバックアップを取得する機能があります。

[Using Amazon S3](https://docs.gitlab.com/ce/raketasks/backup_restore.html#using-amazon-s3)

ドキュメントの記述は以下の様になっていて、バケットは指定できてもディレクトリは指定できない様に見えます。

```ruby
gitlab_rails['backup_upload_connection'] = {
  'provider' => 'AWS',
  'region' => 'eu-west-1',
  'aws_access_key_id' => 'AKIAKIAKI',
  'aws_secret_access_key' => 'secret123'
  # If using an IAM Profile, don't configure aws_access_key_id & aws_secret_access_key
  # 'use_iam_profile' => true
}
gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
```

通常は困りませんが、複数のオンプレGitLabのバックアップを1つのバケットに置きたい場合、どれがどのバックアップなのかわからなくなってしまいます。具体的にはGitLabのバックアップファイルは以下の様な名前になります。

```
1578819848_2020_01_12_12.1.6_gitlab_backup.tar
```

この様に、何らかのIDと日付とバージョン_gitlab_backup.tar となるため、同一バージョンが複数ある場合見分けがとてもつきにくくなってしまう問題がありましたので、調べたところ以下の記述を発見しました。

[stack overflow - Gitlab backup to S3 bucket](https://stackoverflow.com/questions/44505984/gitlab-backup-to-s3-bucket)

試したところ、問題なく動作したのでこちらに書きました。これで快適なGitLabバックアップライフが送れますね、エンジョイ！
alpineLinuxとは

#alpineLinuxとは
今回はdocker imageを作成する際に
Linuxディストリビューションを選択する案として有力なalpineについて書きます。

#<b>特徴</b>
alpineLinux(アルパインリナックス)
musl libc , BusyBoxを基に構成。
軽量でセキュアなLinux。
組込み系にも使われている。

<b>とにかく軽い！</b>

#<b>比較 docker image</b>

```
ubuntu 64.2MB
alpine 5.55MB
centos 220MB
```
<b>メリット</b>
docker pull push などの時間が短縮されるなどのメリットが！



#使い方
<b>パッケージマネージャー `apk`</b>
ちなみにubuntuでは`apt-get`
centosでは`yum `なので注意！


<b>インストール　</b>

```
apk add
```
<b>インストール済みパッケージの確認 </b>

```
apk info
```
<b>パッケージのアップデート</b>

```
apk update
```
<b>パッケージの検索 </b>

```
apk search
```
<b>パッケージの削除 </b>

```
apk del
```


#<b>注意点</b>
bashがデフォルトでないのでashを使うようにしましょう。

```
docker run -it alpine /bin/bash
```
↓↓

```
docker run -it alpine /bin/ash
```
ちなみにbashもインストールすれば使えます!
#はてなブログもやっているのでぜひみてください。
こちらがURL⬇︎
https://taitoajiki.hatenablog.com/
MonacaのアプリをCircleCIとDeployGateで自動デプロイした話

# 背景
Monacaで作成中のアプリの試験的配布を自動化したかった
↓
そんな中CircleCIとDeployGateでアプリのリリース自動化という記事を色々見つけた
↓
そこに検索ワードとしてMonacaやCordovaを足したらそれっぽい記事が見つからなかった....
↓
ので頑張った
(作った後に[Monaca CI](https://ja.monaca.io/ci.html)の存在に気付いた...)

# 目的
Vue.jsにて開発しているMonacaアプリについて
ローカルで作成して
GitHubにpushして
CircleCIでビルドして
DeployGateでデバッガに配布したい

......とりあえずAndroid版だけ


# 内容
端的に言えば
CircleCIとDeployGateのアカウントを無料のでいいので取得し、GitHubと連携
後はリリースしたいリポジトリに下記ファイルを置けば動く
最後の行の[トークン]と[ユーザー名]は自分のDeployGateの情報にて書き換える

``` yaml:.circleci/config,yml
version: 2
jobs:
  build:
    docker:
      - image: circleci/android:api-28-alpha

    working_directory: ~/repo # 作業フォルダ

    steps:
      # ビルド環境構築
      - run:
          name: apt-get upgrade
          command: |
            sudo apt-get update
            sudo apt-get upgrade -y

      - run:
          name: Setup Node.js via nvm
          command: |
            curl --silent -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
            echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
            echo 'source "$NVM_DIR/nvm.sh" --no-use' >> $BASH_ENV 
            source $BASH_ENV
            nvm install v10.15.3 --no-progress
            nvm alias default v10.15.3
            sed -i -e 's/ --no-use//' $BASH_ENV
            
      - run:
          name: Install Cordova
          command: |
            npm install -g cordova
            sudo apt-get install gradle -y
            gradle -v
    
      - checkout

      # キャッシュがある場合はキャッシュから依存関係を復元
      - restore_cache:
          # 復元するキャッシュのkey
          keys:
            - v2-dependencies-{{ checksum "package-lock.json" }}
            - v2-dependencies-
      
      # Cordovaのビルド設定
      # この作業はチェックアウトしてCordovaのプロジェクトが出来てから行う必要がある
      - run:
          name: Setup Cordova to build as Android app
          command: |
            cordova platform add android@8.0.0
            gradle dependencies
    
      # 依存関係のインストール
      - run:
          name: Install dependencies
          command: npm install

      # ビルド
      - run:
          name: PreBuild
          command: npm run build

      # ビルドの確認
      - run:
          name: Check dist
          command: ls -la www

      # キャッシュの保存
      - save_cache:
          paths:
            - node_modules
          key: v2-dependencies-{{ checksum "package-lock.json" }}

      # ビルド
      - run:
          name: Cordova Build
          command: cordova build --debug --device

      # デプロイ
      - run:
          name: Upload to DeployGate
          command: |
            APK_PATH=platforms/android/app/build/outputs/apk/debug/app-debug.apk
            COMMIT_HASH=$(git log --format="%H" -n 1 | cut -c 1-8)
            curl -F "file=@${APK_PATH}" -F "token=[トークン]" -F "message=Test Build by CircleCI <${COMMIT_HASH}>" https://deploygate.com/api/users/[ユーザー名]/apps
```

# まとめ・所感
これで手元のVSCodeからPushするだけでDeployGateからアプリ配布の通知が来てインストールできるので超便利になった

ビルドしたアプリがVue製のためタグにVueがとりあえず入っているけれど、ビルド環境の内容としてはReactでもAngularでも変わらないかと
Herokuへのデプロイ時に発生したエラーの解決策

## django-herokuをインストール
> [Python] プログラム初心者のためのWebアプリ簡単作成法
> https://qiita.com/okoppe8/items/4cc0f87ea933749f5a49

上記を参考にHerokuへのデプロイを試していたところ、 `django-heroku` インストール時にハマりました。

```
pip install gunicorn django-heroku
```

## エラー

```
Could not fetch URL https://pypi.org/simple/django-heroku/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/django-heroku/ (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.")) - skipping
ERROR: Could not find a version that satisfies the requirement django-heroku (from versions: none)
ERROR: No matching distribution found for django-heroku
```

## 解決策
以下で解決しました。

```
brew reinstall python
```

## 参考
SSL module in Python is not available (on OSX)
https://stackoverflow.com/questions/58280484/ssl-module-in-python-is-not-available-on-osx
【Python】リストとタプルの違いを理解する。

# 目的
Pythonの組み込み型である[リスト]と[タプル]の違いについて理解を深めるため。

# リストとタプルの違い

下記の出力結果をみる限り()と[]の違いしかありません。
定義方法やデータへのアクセス方法も似ています。

## リスト
他のプログラミング言語の「配列」の存在に近い。

```python
week_list = ['月曜日', '火曜日', '水曜日']

print(type(week_list))
# <class 'list'>

print(week_list[0])
# '月曜日'
print(week_list[1:])
# ['火曜日', '水曜日']
```

## タプル
Python における「タプル」とは、複数のデータを一直線に並べた「コレクション」タイプのデータ型です。

```python
week_tuple = ('月曜日', '火曜日', '水曜日')

print(type(week_tuple))
# <class 'tuple'>

print(week_tuple[0])  
# '月曜日'
print(week_tuple[1:])
# ('火曜日', '水曜日')
```

## リストとタプルの明確な違い
では、リストとタプルの違いとはなんでしょうか。
それはリストはオブジェクト作成後にオブジェクトidの変更なしに要素を追加・変更・削除が可能(ミュータブル)ですが、タプルはオブジェクト作成後にオブジェクトidの変更なしに要素を追加・変更・削除が不可能(イミュータブル)でないということです。

### イミュータブルとは
イミュータブルとはオブジェクトidの変更なしに追加・変更・削除がでいないと上記で説明しましたが勘違いされやすいポイントが二つあります。

##### 1.
Q. イミュータブルオブジェクトは要素を変更できないのか？
A. オブジェクトidが変わってしまうが要素を変更可能

```python
# リスト型がミュータブルであることを確認
fruits_list = ['みかん', 'りんご']
print(id(fruits_list))
# 4417466768

fruits_list += ['バナナ', 'パイナップル']
print(id(fruits_list))
# 4417466768
# 要素を追加したがオブジェクトidが変わっていない

# タプルがイミュータブルであることを確認
fruits_tuple = ('みかん', 'りんご')
print(id(fruits_tuple))
# 4417176592

fruits_tuple += ('バナナ', 'パイナップル')
print(id(fruits_tuple))
# 4417197744
# 要素を追加したためオブジェクトidが変化

```

> イミュータブルオブジェクトは累算代入(`+=`など)の演算メソッド(`__iadd__`など)を持っておらず、通常の加算演算(`__add__`)に置き換えられます。

累算代入演算子を呼び出した時にリストなどのミュータブルオブジェクトは`.__iadd__`がイミュータブルオブジェクトは`.__add__`が呼び出されていたので結果としてオブジェクトidの振る舞いに違いが出ていたのですね。



##### 2.
Q. オブジェクトidを変えずに要素を変えることは不可能なのか？
A. イミュータブルの要素にミュータブルな要素を持つことで要素の追加・変更・削除は可能

```python

# dictを要素に持つタプルを定義
cards_tuple = ({'mark': 'ハート', 'number': '7'}, {'mark': 'スペード', 'number': '2'})
print(id(cards_tuple))
# 4417441456

# dict を変更する
cards_tuple[0]['mark'] = 'スペード'
cards_tuple[1]['number'] = '13'

print(cards_tuple)

print(id(cards_tuple))
# 4417441456
# 変更後もオブジェクトidが変わっていない
```

# まとめ
基本的にはリストを使っていき、イミュータブルであることが利益となる場合にタプルを使用するようにしていこうと思いました。
tupleには他にもhashableであるという特徴もあるようです。

# 参考
[I'm able to use a mutable object as a dictionary key in python. Is this not disallowed?](https://stackoverflow.com/questions/4418741/im-able-to-use-a-mutable-object-as-a-dictionary-key-in-python-is-this-not-disa)

[https://docs.python.org/ja/3/reference/datamodel.html#object.__iadd__](https://docs.python.org/ja/3/reference/datamodel.html#object.__iadd__)
GitHub Package RegistryのDockerイメージは認証無しではPullできない (2020/01/12時点)

ハマる人がいるかもしれないのでMEMOです。

2020/01/12時点ではGitHub PackagesのDockerレジストリ(docker.pkg.github.com)に登録されているDockerイメージはdocker loginによる**認証が必須**となります。**レポジトリがPublicかPrivateに関わらず認証が必須**です。

このため例えばGKE等でDocker Hub上のDockerイメージと同じようにPullをしようとすると`no basic auth credentials`というエラーが発生してPullに失敗します。

以下はGKE上でエラーが発生した時のPodのエラーメッセージ例です。

```
  Warning  Failed     58s (x3 over 100s)  kubelet, gke-cluster-xxx  Failed to pull image "docker.pkg.github.com/xxx/xxx/xxx:latest": rpc error: code = Unknown desc = Error response from daemon: Get https://docker.pkg.github.com/v2/xxx/xxx/xxx/manifests/latest: no basic auth credentials
  Warning  Failed     58s (x3 over 100s)  kubelet, gke-cluster-xxx  Error: ErrImagePull
```

## 今後の対応予定

ちなみに本件についてはGitHub側は仕様であると回答してますが、認証無しの要望についてはGitHub Package Registryチームに展開したと述べています。このため将来的にはPublicレポジトリについては認証無しでもPullできるようになるかもしれません。

https://github.community/t5/GitHub-Actions/docker-pull-from-public-GitHub-Package-Registry-fail-with-quot/td-p/32782
cssでのブロックの配置方法

#1. 目的

Webページを作成するときにブロックを配置する方法をcss/htmlでどのように記述するかを記録する。


#2 内容

作成したWebページ

![14.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/e2706ceb-e582-2d79-18b2-414e0b5d0ad1.jpeg)


上記を実現するコード

```html:sample.html
<div id="large_block">
      <div class="float150">
      </div>

      <div class="float300">
      </div>
</div>

<div id="large_block">
      <div class="float150">
      </div>

      <div class="float300">
      </div>
</div>
```

```css:sample.css
#large_block {
    width: 1000px;
    height: 200px;
    padding: 0px;
    margin: 10px;
    float :left; /* 左側を起点にする */
    border: solid 0.5px; /* 領域のボーダーラインの設定 */
}   

.float150 {
    height: 150px;              /* 高さ指定 */
    width: 150px;               /* 幅指定 */
    background-color: #FFF;     /* 背景色指定 */
    margin:  23px;              /* 周りの余白指定 */
    float:  left;   /* 回り込み指定(左から順番に配置していくということ) */
    border: solid 2px #6091d3;/*線*/
    border-radius: 10px;/*角の丸み*/
    padding: 0px;
}

.float300 {
    height: 150px;              /* 高さ指定 */
    width: 300px;               /* 幅指定 */
    background-color: #FFF;     /* 背景色指定 */
    margin:  23px;              /* 周りの余白指定*/
    float:  left;   /* 回り込み指定(左から順番に配置していくということ) */
    border: solid 2px #6091d3;/*線*/
    border-radius: 10px;/*角の丸み*/
    padding: 0px;
}
```
RailsのActiveStorageで画像が表示されずTypeError(no implicit conversion of nil into String)がでるようになった

**2020/1/14更新　Rack 2.1.1で修正されたようなので、現在は同様の問題は発生してません**
# 状況
ローカルで開発していたアプリをDockerで構築し直していたらActiveStorageを使用して表示していた画像が表示されなくなった
ログを見ると以下のようにTypeError (no implicit conversion of nil into String)が発生してる

```console
Started GET "/rails/active_storage
～ (省略)～
Processing by ActiveStorage::DiskController#show as PNG
Parameters: {"content_type"=>"image/png", "disposition"=>"inline;
～(省略)～
Completed 500 Internal Server Error in 1ms (ActiveRecord: 0.0ms)

TypeError (no implicit conversion of nil into String):

rack (2.1.0) lib/rack/files.rb:25:in `expand_path'
rack (2.1.0) lib/rack/files.rb:25:in `initialize'

```

# 原因
最新版のRack(2.1.0)で変更された部分が原因

[Rails “TypeError (no implicit conversion of nil into String)” when loading images using image_tag, activeStorage and “has_many_attached”](https://stackoverflow.com/questions/59691691/rails-typeerror-no-implicit-conversion-of-nil-into-string-when-loading-image)
[Rack 2.1.0 breaks ActiveStorage #1464](https://github.com/rack/rack/issues/1464)

# 解決策
次のマイナーアップデートで修正されるみたいですが取り急ぎはダウングレードすればよい
Gemfileを編集して

```:Gemfile
gem 'rack', '~> 2.0.8'
```
bundle updateで解決
組織の成長度におけるマネージャーに求められるスキル

#1．概要
よい組織を作るにはマネージャーの存在が不可欠だと思います。その中で、これまでの経験や書籍を読んでいる中で考えた、組織の成長段階によって求められるマネージャーのスキルを記載したいと思います。

マネージャーがうまく機能している会社は、おそらく以下の順序で成長していくと思います。

###①混乱期
マネージャーがおらず、ギスギスした環境となっている。マネジメントを始める段階。心理的安全性を高め、なんでも言い合える環境を作る必要がある。

###②闘争期
心理的安全性が高まり、なんでも言い合える環境となった。ただ、感情的な争いも増えている。できる限り感情的な争いを建設的な争いに変える必要がある。

###③成長期
建設的な話が増え、いい環境となっている。今後、さらなる成長を則すための環境づくりが求められる。

###④決断期
組織が成長し建設的な話題も増え、どんどんいい環境となっている。ただ、どうしても複数のいい案の中から、1つの案を選択しなければならない時が来る。その時に、マネージャーはきちんと選択できなければならない。

#2．混乱期
マネジメントが行われていない、もしくはマネージャーが不在の状況となっているのが、最初の段階です。
この中でマネージャーの第一の仕事は、**現場の心理的安全性を高め、お互いが安心して言い合える環境を作り出すこと**です。そのための必須スキルが**「傾聴」**になります。

相手が何を思っているのか、どんな課題を抱えているのか、なんでも言い合えるように「いってもいいんだよ」ということを伝え、聞いてあげることが重要です。

そのため、私がよくやっていたのは、

- 笑顔で話を聞く

ネガティブな話であれ、感情的な話であれ、けんかにならないように笑顔であることを心がけました

- 質問してきた場合、丁寧に返答する

何を言ってもいいよ、と伝えるために相手から何か質問が来た場合、どんなに些細なことでも必ず丁寧に返答するようにしました。

また、傾聴ではないのですが以下のようなこともやっていました。

- 自分の失敗談を話す

失敗したことを話しても問題ないよ、と伝えるために自分の失敗談を話します。よく話すのは、超大手の会議で取締役、部長陣10名ほどいる中、なぜか私一人（社会人歴3年目、平社員）で相手の組織の問題点をプレゼンし「御社はいったい何を考えているのですか？」と詰められ、しばらくまともに業務をできなかったたことです…

#3．闘争期
お互いが言い合える環境ができた場合、いい意味での争いが多く発生します。その良い争いが続くのであれば問題ないのですが、大抵**感情的な争い**も発生し始めます。

そのため、第二に求められるスキルは、感情的な争いを建設的な争いに変えるための**「ファシリテーション」**スキルになります。

感情的な話題を建設的に変えるために、以下のようなことが必要と考えます。

- どうしてほしいのか聞き、相手の行動になるまで話をする

「あいつは全く人の話を聞かない！」⇒「○○さんは△△さんに話を聞いてほしいの？」
「聞いてほしいけど、そんな機会はない！」⇒「なら、機会があれば話してみる？」
「でもけんかになるかもしれないし。。。」⇒「なら、私が間に入りましょうか？」
「それならいいかも。。。」⇒「なら、その会議を設定してくれる。△△さんには私から事前に話すから」
**「わかりました。会議の設定をします。」**

**※現実的にはこんなに単純ではなく、一度の行動を起こさせるのに30分以上会話します…**

- どうしてもけんかになるなら、別々に聞きに行く

当事者同士をあつめて解決できるならいいのですが、感情的な争いではそれもできない可能性があります。そのため、まずは2人を別々な場所でヒアリングし、お互いどうしてほしいのか聞きます。そして、お互いの落としどころを探ります。



#4．成長期
建設的な話が多くなってきた後は、「どのようにすればもっと相手が伸びてくれるのか」を考えるようになります。そのため、第三に求められるスキルは**「コーチング」**スキルになります。

###目標の立て方
基本的には相手に目標を立てさせ、目標をもとにコーチングを行いますが、以下の点に注意ください。

- 目標は変わってもいい

一度立てた目標を何が何でもやらせようとする人もいますが、変えてもいいことを伝えてください。
⇒いきなり立てろと言っても立てられる人はほとんどいない＋目標に縛られるようになる

- 目標は7割程度でできるもの

目標は大きくもなく小さくもないものを設定してください
⇒大きいと達成できずモチベーションが下がります。また、小さすぎるとすぐにできるのでやることの価値を見出せず、こちらもモチベーションが下がります。

###コーチングで行ったこと
そしてコーチングする際は、以下のようなことを実施していました。

- 現状のフィードバックを行う

目標を達成できたのかどうか、できないなら何が要因か、自分の視点も含めフィードバックを行います
⇒1人だと周りが見えなくなって、なぜ間違えたのか理解できないことがあります。（感情的にならないようにファシリテーションを意識してください）

- 自分の目標が合っていたか確認する

一度立てた目標が、本当に本人に合っていたかどうか確認してください。
⇒目標が最初にキチンと決まることは、ほとんどありません。


#5．決断期
そして最後に求められるスキルは**「リーダーシップ」**になります。どんなに建設的な話を行っていても、どんなに環境が良くても、決断をしなければならない時が必ず来ます。

その時は、どんなにいい意見でも、採用するしないを決めてください。そうしなければ、ずるずる会議が伸び、結果として双方身動きが取れなくなることが多いです。

また、私が決断を下すときは、必ず最初に立てた「目標」をもとに決断を下しています。例えば、「現場が楽しくなるような仕組みを作りたい」だった場合、

「双方を比較して○○の方が、～という理由で楽しくなる仕組み強いので、こちらを採用する」

この際、必ず決めた根拠を説明し、採用しなかった方に必ずフィードバックを行ってください。この振り返りの経験は、次の提案に必ず生きてきます。

#6．まとめ
いかがでしょうか？
まとめると、個人的なマネージャーのスキルの優先度は以下となります。

1．傾聴
2．ファシリテーション
3．コーチング
4．リーダシップ

あくまで1つの目安ですが、今後のマネジメントの助けになれば幸いです。
クラスまとめ②


##【メンバへのアクセス制限】
→クラスの外からアクセスできないメンバをprivateメンバという
→メリットは意図しない値の代入を防ぐ
→主に変数に使用

```
//メンバの前にprivateをつける
class test{
private int num;
private String name;
}
```
→クラスの外からアクセスできるメンバをpublicメンバという
→主にメソッドに使用

```
class test{
//private変数
private int num;
private String name;

//publicメソッドからprivate変数に代入することで意図しない代入を防ぐ
public void setDate(int n, String x){
 if(n > 0 && x.length){
  this.num = n;
  this.name = x;
 }
  else{
   Systm.out.println("正しい値ではありません");
  }
}
}
```

→フィールドにprivate、メソッドにpublicをつけることにより、誤りが起きづらくなる手法をカプセル化という
→メンバに修飾子をつけない場合は同じパッケージ内ならどこからでもアクセスできるようになる


##【メソッドのオーバーロード】
→同クラス内に同名のメソッドを二つ以上作成する
→引数の種類、または個数は一つの固有値である必要がある
→受け取る引数に適したメソッドが呼ばれる

```
//同名のメソッドを複数作れる
//メソッド１を呼びたければint型の引数を一つ渡す
//メソッド２を呼びたければint,String型の引数をひとつずつ渡す
//メソッド３を呼びたければboolean型の引数を一つ渡す
class test{
//Carメソッド１
public void Car(int n){
System.out.println("残りのガソリンは"+n+"Lです");
}

//Carメソッド２
public void Car(int n,String name){
System.out.println("残りのガソリンは"+n+"Lです");
System.out.println("この車は"+name+"さんのものです");
}

//Carメソッド3
public void Car(boolean a){
if(a == true){
System.out.println("この車は車検に通っています");
}
else{
System.out.println("この車は車検に通っていません");
}
}
```

##【コンストラクタ】
→クラスの初期設定
→クラス名と同名にする必要がある
→オブジェクトが作成された段階で自動的にコンストラクタ内に記述された処理がされる

```
//クラスの初期設定を行う
class Car{
//フィールド
private double engine;
private int num;

//コンストラクタ
public Car(){
this.gus = 0;
this.num = 1234;
System.out.prinln("車を作成しました,ナンバーは"+num+"番です。");
}
}

//オブジェクトを作成した段階でコンストラクタが自動的に処理する
public class Main{
public static void main(String[] args){
 Car car1 = new Car(); // この段階でコンストラクタの処理が行われる
                       //「車を作成しました,ナンバーは1234番です」と表示

System.out.println("ガソリン量は"+car1.gus+"Lです");
//「ガソリン量は0Lです」と表示
}
}
```
→コンストラクタもメソッド同様にオーバーロードでき、引数を渡すところでどのコンストラクタを呼ぶか区別する

→　```this();```　をコンストラクタ内の先頭に記述することで他のコンストラクタを呼び出せる（上記同様引数でどのコンストラクタかを区別する）

→コンストラクタを定義しないとデフォルトコンストラクタという引数のないコンストラクタが定義される

→修飾子(private,public)をつけることができ、privateをつけることでクラス内で他のコンストラクタに受け継がせるのみの機能をつけられる

##【クラスメンバ、インスタンスメンバ】
###(インスタンスメンバ)
→オブジェクトを生成したときに利用できるメンバをインスタンスメンバという
→これは個々オブジェクトに関連するメンバであるために固有性、および具体性が高い
→呼び出し方は　```オブジェクト名.メンバ```　※インスタンス化する必要あり

###(クラスメンバ)
→クラス全体に関連するメンバ
→インスタンスメンバであれば例えば車Aや車Bと言った固有のオブジェクトに関連するが、それに対してクラスメンバは車クラス全体のデータを管理するのに用いることができる
→static修飾子をつける
→呼び出し方は　```クラス名.メンバ```　※インスタンス化する必要はなし

```
class Car{
//クラスフィールド
public static int sum; 

//インスタンスフィールド
private int num;
private double gas;

//コンストラクタ
public Car(){
num = 0;
gas = 0;
sum++;
System.out.println("車を生成しました");
}

//クラスメソッド
public static void showSum(){
System.out.println("車は全部で"+sum+"台です");
}
}

class Main{
public static void main(String[] args){
Car.showSum(); //クラスメソッドを呼び出す
　　　　　　　　//出力結果 「車は全部で０台です」

Car car1 = new Car(); //オブジェクト生成

Car.showSum(); //クラスメソッドを呼び出す
　　　　　　　　//出力結果 「車は全部で１台です」

}
}
```

「※クラスメンバの注意事項」
→クラスメソッド内では ```this.```によって変数を指定できない
→クラスメソッド内ではインスタンスメンバにアクセスできない
[Nuxtjs] base64エンコードする画像のファイルサイズの上限を変更する

## TL; DR
 - `nuxt.config.js` のbuildプロパティで、base64エンコードできる画像のファイルサイズの上限を変更できる。

## 概要
 - nuxtjsでwebサービスの自社プロダクトの開発と運用をやっているエンジニアです。
 - ある日、ロゴの画像を変更してほしいとデザイナーから依頼が来ましたが、`static`ディレクトリにある画像を差し替え社内開発環境にリリースしまいしたが、ブラウザキャッシュが効いてしまい、昔のロゴが表示されてしまいました。
 - 何かクエリバラメーターをつけるなどして、画像のパスを変更すれば解決するのですが、何か別の解決方法がないかを探したところ、assetsに静的ファイルをおけば、webpackが静的ファイルをbase64エンコードし、htmlやcssに埋め込みをしてくれるみたいでした。が、試したところ、どうやら[base64エンコードしてくれる画像のファイルサイズがデフォルトで1KBと決まっている](https://ja.nuxtjs.org/guide/assets/#webpack)みたいなので、変更したいと思ったのですが、やり方を探しても見つからなかったので、せっかくならと思い、記事にまとめました。
 
## 実際にどうやるか
結論から話すと、下のように書くと、base64エンコードしてくれる静的ファイルのサイズ上限を変更できました。

```nuxt.config.js
module.exports = {
  extend(config, { loaders }) {
    // これで1MB以下のassetsディレクトリにある静的ファイルがbase64エンコードされます。
    loaders.imgUrl.limit = 100000
  },
}
```

この `loaders` と `imgUrl` がどこから来たからというと、下のドキュメントに記載されていました。
https://ja.nuxtjs.org/api/configuration-build/#loaders

```js
// loadersの中身
{
  file: {},
  fontUrl: { limit: 1000 },
  imgUrl: { limit: 1000 }, // デフォルトは1KB
  pugPlain: {},
  vue: {
    transformAssetUrls: {
      video: 'src',
      source: 'src',
      object: 'src',
      embed: 'src'
    }
  },
  css: {},
  cssModules: {
    localIdentName: '[local]_[hash:base64:5]'
  },
  less: {},
  sass: {
    indentedSyntax: true
  },
  scss: {},
  stylus: {},
  vueStyle: {}
}
```

全部のコードを追えていないため、 `imgUrl` のプロパティがどう`url-loader` の設定と紐づいているかまではわかっていないのですが、プロパティの名前から予測することができたため、試したところうまくbase64エンコードされました。

ちなみに、base64エンコードされるとhtmlではこのようになります。

<img width="716" alt="スクリーンショット 2020-01-12 17.27.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152032/80d2ff6f-38b4-439f-ac25-259fbdd02fd6.png">

## まとめ
 - これで今後画像のパスを無理やり変更しなくても、画像によってエンコードした結果が変わるので、ブラウザキャッシュでファイルの中身が変更されないという問題は回避されるのかなと思いました。内容が少しニッチな気持ちもしますが、同じ問題で困った人の役に立てればと思い、qiitaにまとめました。最後まで読んでいただき、ありがとうございました。
JavaScript勉強の記録その8: スプレッド構文＆レスト構文を利用した配列オブジェクトの操作

#スプレッド構文を利用して２つの配列を結合

const/let 定数名/変数名 = [...配列名]とすることで、２つの配列を結合することができます。
例としては、以下のような２つの配列があった場合は、const b = [55, 80, ...a]と記述することで、２つの配列を結合することができます。　
この方法をスプレッド構文と呼びます。

```index.js
const a = [10, 40, 12, 50];
const b = [55, 80, ...a]; 

console.log(b);
//[55, 80, 10, 40, 12, 50]
```

#レスト構文を利用して、配列の要素を別の定数へ代入
配列の要素を別の定数に代入したいという場合はレスト構文が役に立ちます。

下の例ではconst [a, b, ...other] = scores2とすることで、scores2の１番前の要素から順にaとbに要素を代入しています。
scores2の残りの要素は、otherという定数に配列として格納されます。これをレスト構文と呼びます。

ちなみに配列の要素を別々の変数や定数に格納することを分割代入と呼びます。

```index.js
const scores1 = [10, 40, 12, 50];
const scores2 = [55, 80, ...scores1];
const [a, b, ...other] = scores2; 

console.log(a);
//=>55
console.log(b);
//=>80
console.log(other);
//=>[10, 40, 12, 50]
```
CentOS 古い kernelを整理する & 増殖させない

# この記事に書いてある事

* updateを繰り返しているうちに増えてしまう kernelを整理する
* 増えすぎないように設定する方法

## 増えすぎたkernelの整理

* CentOS6 / 7
    * package-cleanup コマンドを使って整理  
    kernelを新しい方から二つ残して後を消す場合

    ```shell
    [user01@centos7 ~]$ sudo package-cleanup --oldkernels --count=2
    ```

* CentOS8
    * dnf で installonly_limit 以上に古いパッケージを削除する  
    コマンドラインに kernel に関する設定はされていませんが、大抵の場合 installonly に設定されているのはkernel関連だけです。  
dnf repoquery --installonly で確認後にremove --oldinstallonly を実行してください。  
ただ、手元のCentOS8はinstallonly_limit=3に設定されているのであまり整理が必要な状況にならない気がします。

    ```shell
    [user01@cent8 ~]$ dnf repoquery --installonly
    [user01@cent8 ~]$ sudo dnf -y remove --oldinstallonly
    ```
## kernelが増えすぎないように設定する

* CentOS6 / 7
  /etc/yum.conf の installonly_limit= で保持する世代数を指定
  0に設定すると無制限になります。通常 5に設定されているようです。

    ```shell
    [user01@centos7 ~]$ sudo vi /etc/yum.conf
    [main]
    cachedir=/var/cache/yum/$basearch/$releasever
    keepcache=0
    debuglevel=2
    logfile=/var/log/yum.log
    exactarch=1
    obsoletes=1
    gpgcheck=1
    plugins=1
    installonly_limit=5
    bugtracker_url=http://bugs.centos.org/set_project.php?project_id=23&ref=http://bugs.centos.org/bug_report_page.php?category=yum
    distroverpkg=centos-release
    
    ```

* CentOS8
  /etc/dnf/dnf.conf の installonly_limit= で保持する世代数を指定
  ただし 1,0 に設定すると無制限とみなされます。2が最低の設定値になります。

    ```shell
    [main]
    gpgcheck=1
    installonly_limit=3
    clean_requirements_on_remove=True
    best=True
    [user01@cent8 ~]$ 
    ```
C/C++ コードの clang fuzzer テストを整備するメモ

## 背景

* C/C++ で画像やレンダリングシーンデータのバイナリデータを読んだり, 文字列処理をしたりしている
* バグがないかテストケースをたくさん書くのがめんどくさい
  * fuzzer についてはなんとなく知っている
    * 時々 tinyexr とかで fuzzer でレポートがきます https://github.com/syoyo/tinyexr/issues/107 

fuzzer があります. 

ご注文は American Fuzzy Lop ですか？
https://qiita.com/binzume/items/8e985e7bd01a6e4000d3

Clangでファジング (-fsanitize=fuzzer)
https://qiita.com/snsinfu/items/67bd5196a53e5575c4b9

最近は Clang にデフォルトで取り込まれました(以前は libFuzzer.a をリンクなどが必要だった)

Ubuntu 18.04 では, apt で入る clang-8 でいけます. 便利な世の中になりましたね
 
```
$ sudo apt install clang++-8
$ sudo apt install libfuzzer-8-dev
```

必要に応じて, clang++ で clang++-8 が呼ばれるようにしておきます.

```
$ sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 10
$ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 10
```

あとは

http://llvm.org/docs/LibFuzzer.html

あたりを参考にして, fuzzer テストを回すプログラムを書いていけばよいです.
基本的には address sanitizer, memory sanitizer を有効にします.

cxx option いろいろ変えたりするので, サードパーティライブラリとたくさんリンクしている場合はビルド設定が面倒になりますので, うまく最小で再現するようなコードにするのがよいでしょう.

## 注意点

ubuntu apt などで入る clang では runtime/abi を既存の gcc toolchain で賄うためか, うまく log(backtrace) を出してくれないケースがあります
(segmentation fault だけ出す. たとえば関数を無限に再帰的に呼んで stack overflow になるときなど)

https://releases.llvm.org/download.html

から得られるパッケージを使うとよさそうです.

## 実行する

* -jobs=8 などで並列実行するとよいでしょう.
* コードにもよりますが, 画像やシーンデータとかですと入力データサイズは GB も普通なので, `-rss_limit_mb=10000` などとして, でメモリ上限をあげましょう.

### Copus

PNG, JPEG などフォーマットが決まっているような場合, Copus データとしてそれらを与えることでより効率的に fuzzing test することができます.

https://llvm.org/docs/LibFuzzer.html#corpus

## Fork mode

2022/09 時点では experimental ですが, fork モードでいくつかパターンを分岐させて処理できます.

https://llvm.org/docs/LibFuzzer.html#fork-mode

## oss-fuzz

OSS であれば github で PR でリクエストを送れば, GCE あたりでクラウドで実行してくれる oss-fuzz サービスがあるようです.

https://github.com/google/oss-fuzz

## fuzzer で不都合が見つかったら?...

fuzzer はバグが再現するテストデータを作ってくれるだけなので, コードをどう修正するかはコード作成者次第です.

何度か fuzzer で見つかる不都合を見ていると, どうコードを直すか(値の範囲チェック, out-of-bounds アクセス, etc)がわかってきますので, それを参考にロバストなコード(きちんとエラーチェックとかする)を書いていきましょう.

tinyexr のときに経験しましたが, 毎回 fuzzer で見つかった不都合部分にチェックを入れてその場その場で直すのは手間で, 一から書き直したほうが手っ取り早く感じるケースもあります(tinyexr の場合は書き直すのも, めんどうなのでなかなかとりかかりには至っていませんが...)

本来起きないはずだったり, 正しいのではあるが fuzzer, sanitizer が検出できるメモリサイズを超えている(e.g. 4GB)ででエラーになっているだったりとか, 原因は third-party のコードにあり修正が難しい(他人のコードの解析がめんどい), など, どう判断するのがよいのかに困るものもあったりします.

## TODO

* [ ] 見つかった crash レポートを入力させて regression させる仕組みをつくる(fuzzer でビルドされたプログラムには指定できないっぽい?)
* [ ] ClusterFuzzer を手元計算機環境で回したい(デフォルトの設定は GCE 限定っぽいがいろいろ書き換えればいけるはず)
* [ ] 機械学習あたりでこのコードはこう直すとよいよ, という助言とかしてほしい
Xorshift から派生した擬似乱数生成器

# 追記

別途、 [C++ での乱数の選択]( https://qiita.com/Nabetani/items/78f14d29e49dbc07945f ) という記事を書いているので、そちらも合わせてお読みください。

# 乱数の種類

コンピュータで扱える乱数は、3種類ある

|名前(?)|説明|
|:--|:--|
|ハードウェア乱数生成器|本物の乱数。特別なハードウェアが必要。|
|暗号論的擬似乱数|計算で作る。過去の乱数列から次の値を予測できないことが重要。|
|普通の疑似乱数|計算で作る。得られる数列がバラけていることが重要。|

で。この記事は普通の疑似乱数の話。

# 今までの乱数生成器の選択

私は主に2つの乱数生成器を使っていた。

|名前|バラケ具合|周期|計算量|
|:--|:--|:--|:--|
|メルセンヌ・ツイスタ(`mt19937`)|とても良い|$2^{19937}-1$|やや多め|
|Xorshift128|良い|$2^{128}-1$|少なめ|

mt19937の周期は1秒間に無量大数個の無量大数倍の乱数を発生させるコンピュータがあっても、無量大数年の無量大数倍を遥かに超える周期になっている。周期の長さを日本語で表現するのが困難である。

一方。
Xorshift128の周期 は $2^{128}-1 ≒ 3.4×10^{38}$ で、mt19937 より遥かに短い。
それでも十分長い。
1秒間に$10^{15}$個(千兆個。1ペタ個)の乱数を発生させると $10^{16}$年、つまり 1京年ぐらいで一周する。
日本語で表現できる範囲内ではあるものの、この周期で不足する状況を起こすのは難しいと思う。

というわけで

* mt19937 がファーストチョイス。とりあえず mt19937 を使う。
* mt19937 だと計算速度面で問題があるようなら、 Xorshift128 を使う。

という対応を行ってきた。

# xoshiro / xoroshiro ファミリ

たぶん全部 http://prng.di.unimi.it/ に書いてある。

## 特徴

* ビットローテート計算を使う。
* Xorshift ファミリよりバラケ具合が優れている( mt19937 よりもいいかもしれないぐらい )
* 周期は Xorshift と同じ $2^{128}-1$ とかなので十分長い。
* 計算量はXorshift と同じか若干少ないぐらい。

計算量が少ないのは、ビットローテート命令が速いからかもしれない(未調査)。

ビットローテート命令がない CPU だとちょっとつらいかもしれない。

## バリエーション

バリエーションが色々ある

### xoshiro と xoroshiro

xoroshiro (xor-**rotate**-shift-rotate ) は、普通に 32bit/64bit 生成する。
xoshiro (xor-shift-rotate )  は、xoroshiro　より少し速いけどバラケ具合が劣っている。64bit を得てそのうち 53bit を使って浮動小数点数を作ったりするときは xoshiro でも十分良いらしい。

普通は xoroshiro を使えばいいと思う。

### `**` と `++`

`xoroshiro128++` と `xoroshiro128**` がある。

バラケ具合には差がないらしいし、実際に調べた範囲では差がなかった。

ソースコードを見ると一行だけ差がある。
差は以下の通り：

|名前|コード|
|:--|:--|
|xoroshiro128++|`rotl(s[0] + s[3], 7) + s[0];`|
|xoroshiro128**|`rotl(s[1] * 5, 7) * 9;`|

大差ない。
`++` の方が参照している変数の数が多い。
`++` には乗算がないが、`**` には乗算がある。とはいえ、コンパイル時定数の 5 を乗じているので乗算命令に落ちないんじゃないかと思う(未調査)。

パット見、x86-64 なんかでは `**` の方が若干速いんじゃないかと思ったけど、測ってみたら同タイム。バラケ具合も調べた範囲では差がない(両方良すぎてわからない)。
完全にどっちでもいいと思う。

GPU版を作ろうとかいうことになったら向き不向きがあるかもしれない。

### 周期

xoroshiro128 ファミリ と xoroshiro256 ファミリ がある。
周期はそれぞれ $2^{128}-1$ と  $2^{256}-1$ 。

前述のサイトでは

* 普通は 256 で。
* メモリ制約が厳しければ 128 を。

とあるけど、私は 256 を使いたい理由が思いつかないので普通は 128 でいいんじゃないかと思っている。
もっと長い周期が必要な人のために 512 と 1024 もあるみたいだけど、そんな人はいないと思う。

### 実装

それぞれに 32bit CPU用と 64bit CPU用がある。
CPU に合わせて使えばいいと思う。

# 今後の乱数生成器の選択

候補となるのは以下の3つ：

|名前|バラケ具合|周期|計算量|
|:--|:--|:--|:--|
|`xoroshiro128++`|とても良い|$2^{128}-1$|少なめ|
|`xoroshiro128**`|とても良い|$2^{128}-1$|少なめ|
|メルセンヌ・ツイスタ(`mt19937`)|とても良い|$2^{19937}-1$|やや多め|

mt19937 と、xoroshiro128ファミリのバラケ具合の差はわからなかった。
どちらもとても良い。

というわけで。

今後、シミュレーションなどのために乱数生成器を選ぶとしたら以下のようにすると思う：

* `xoroshiro128**` がファーストチョイス。 `xoroshiro128++` でもいい。
* C++ の標準ライブラリで済ませたい場合などは `mt19937` を使うかも。

# 疑問点

Xorshift は「えっくすおあしふと」と呼んでいた。
xoroshiro はなんて読むの？　「ぞろしろ」？
カメラとRaspberry Pi(Jetson)と機械学習を利用したスマート防犯カメラ　アイディア　要件定義

##カメラとRaspberry Pi(Jetson)と機械学習利用したスマート防犯カメラシステム　アイディア
カメラとRaspberry Piと機械学習の組み合わせで、思いついたアイディア
####スマート防犯カメラ使用例

#####マンションで利用
　マンション住民全員顔を登録する。
　マンションの非常階段に設置する。深夜　登録顔以外が階段をいたら、住民全員に警戒メールを送る
　非常階段で人が（登録顔以外が）長時間待機している　通報メールを送る
　オートロック扉が開いた時、登録顔以外が住人と紛れて入ってきたら警戒メールを送る
　ベランダ、庭に人が侵入　登録顔以外だったら　メールを送信　携帯にアラートがなる　Raspberry Piから警告音声
　玄関の鍵、車の鍵ををピッキングしている動作を判断　警報メールを送る

#####家で利用
　家の人の顔を登録する。
　深夜、庭で火を検知　Raspberry Piが消防署と警察署に通報する
　深夜、庭で何かをもの投げた　警戒メールを送る
　家の前に不審な車が何時間も止まっている。Raspberry Piが警察署に通報する　警戒メールを送信
　家の前で何時間もたむろしている人がいる。Raspberry Piが判断　警戒メールを送る
　寝室で寝ている。警戒メールがなる。アプリで確認。侵入者あり　
　Raspberry Piが通報　寝室の扉をオートロックでロックする
　玄関の鍵、車の鍵ををピッキングしている動作を判断　警報メールを送る
　窓が壊された画像から判定　警報　通報メールを送る

#####外出で利用
ショルダーバックに穴を開けカメラとラズベリーを入れる。後ろを撮影　ストーカーされているかどうか機械学習に判断させる　警報メールを送る

#####町内会で利用
　町内会の交差点にカメラを設置　見慣れない車　ナンバー　県外　
　スモークをはっている車が侵入してきたら警戒メールを住人に送る　データがRaspberry Piにも届く

#####見た目　動きの画像で判断
　顔を隠すものをしているかどうか（マスク　サングラス　パンスト　ヘルメット）　警報メールを送る
　万引きする動作　警報メールを送る
　刃物を持っている　警報メールを送る
　何かものを投げ込んだ動作　警報メールを送る

#####特徴
　Raspberry Piが中心　（中央サーバーに画像は送信しない）
　解析はローカルでやる
　ネット環境のない場所で運用することが前提　
　アップデート、パターンデータのみダウンロード
　Raspberry Piが画像の解析と警戒する
　Raspberry Piがメールの送信、通報をやる
　Raspberry PiにローカルWebサーバーを搭載　日報の作成や設定画面に使用する
　望遠カメラモジュールと超広角カメラモジュールで幅広い警戒が可能
　カメラが怪しい人を追尾する
　カメラを縦に３台設置する　全体用　追尾用　望遠用
　360度カメラ　広角モジュールで幅広く警戒　カメラを２つにして180度で鮮明画像で警戒
　秘密の言葉を発するとRaspberry Piが音声認識し警察に通報してくれる
####オプション

　クラウドストレージ(OneDrive Appleストレージ DropBox)は非同期に
　データーをアップロードできる　
　パターンデータを管理会社からダウンロードできる
　アップデートで機能追加

####Raspberry Pi

　望遠カメラモジュールにも対応
　超広角カメラモジュールで360度にも対応
　サーモグラフィ機能　赤外線で夜の画像に対応
　サーモセンサーモジュール　火を検知する
　音声認識モジュール　言葉を感知する
　設定画面をタッチパネルとキーボードで操作できる
　Webサーバー　日報作成し閲覧用　管理画面用
　SDカードを使用できる
　HDD差し替えられる　容量の増量に対応

####専用アプリ　

　詳細設定する画面
　警告メールを受信
　警告アラートを発信
　Raspberry Piが一週間の日報を作成しアプリで閲覧できる(ローカルWebサーバー)

####カメラ　

　望遠カメラ
　超広角カメラ
　隠しカメラ型
　ミニカメラ型
　赤外線カメラ

####その他
　[スマート防犯カメラ](https://qiita.com/raspberryPiLove/items/44ded568020498d988d4)
　[スマート介護カメラ](https://qiita.com/raspberryPiLove/items/9f70c96383e7ccdfcc62)
　[スマート畑盗難防止カメラ]
(https://qiita.com/raspberryPiLove/items/24e81cebde33eb8c8366)

　スマート育児カメラ
　スマート園児カメラ
　スマート小学生カメラ
　スマート病院用カメラ
　スマート害獣カメラ
　スマートペットカメラ
　スマート酪農カメラ
　スマート牧場カメラ
　スマート田んぼ畑監視カメラ
　スマートお留守番カメラ
GCEでPythonのバージョンあげようとしたらはまった話

## TL;DR
他にも`apt get` すべきファイルがあったってこと。
めっちゃ雑に書きます。

## バージョン
```
$ python3 -V
3.5.3
```

## pyenv install 3.7.3
```
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.profile
$ echo 'eval "$(pyenv init -)"' >> ~/.profile
$ source ~/.profile
```


```
jrny@iris:~/Iris$ pyenv install  3.7.3
Downloading Python-3.7.3.tar.xz...
-> https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tar.xz
Installing Python-3.7.3...

BUILD FAILED (Debian 9.11 using python-build 1.2.16-1-g4500a33c)

Inspect or clean up the working tree at /tmp/python-build.20200112073219.22183
Results logged to /tmp/python-build.20200112073219.22183.log

Last 10 log lines:
    sys.exit(ensurepip._main())
  File "/tmp/python-build.20200112073219.22183/Python-3.7.3/Lib/ensurepip/__init__.py", line 204, in _main
    default_pip=args.default_pip,
  File "/tmp/python-build.20200112073219.22183/Python-3.7.3/Lib/ensurepip/__init__.py", line 117, in _bootstrap
    return _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
  File "/tmp/python-build.20200112073219.22183/Python-3.7.3/Lib/ensurepip/__init__.py", line 27, in _run_pip
    import pip._internal
zipimport.ZipImportError: can't decompress data; zlib not available
Makefile:1130: recipe for target 'install' failed
make: *** [install] Error 1
```


なんかうまくいかなかったので、
エラーログをみる。

`/tmp/python-build.20200112073219.22183.log`
にログ貯まるんで、見てみました。

```
Last 10 log lines:
    sys.exit(ensurepip._main())
  File "/tmp/python-build.20200112073219.22183/Python-3.7.3/Lib/ensurepip/__init__.py", line 204, in _main
    default_pip=args.default_pip,
  File "/tmp/python-build.20200112073219.22183/Python-3.7.3/Lib/ensurepip/__init__.py", line 117, in _bootstrap
    return _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
  File "/tmp/python-build.20200112073219.22183/Python-3.7.3/Lib/ensurepip/__init__.py", line 27, in _run_pip
    import pip._internal
zipimport.ZipImportError: can't decompress data; zlib not available
Makefile:1130: recipe for target 'install' failed
make: *** [install] Error 1
```

理解した。
GCEってこんなすっからかんだったんですね。

```
$ sudo apt update
$ sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev libffi-dev liblzma-dev python-openssl
```

なんかとにかく色々入れておきます。

うまくいきました。
【備忘録】IPアドレス, MACアドレス, ポート番号, プロセスid

#はじめに#
色んな値が出てきて何がなんだかって感じなのでメモです。
ざっくりなので詳しい内容は参考にしているURLとかから調べてください。

#IPアドレス#
**Internet Protocol Address**
>インターネット上に接続された機器が持つナンバーのことです。データをやり取りする際、ネットワーク上で通信相手を間違わないようにするために使われます。IPアドレスには種類やルールが存在します。
>https://www.kagoya.jp/howto/network/ipaddress/

つまりは, インターネット上での機器の住所ですね。

#MACアドレス#
**Media Access Control address**
>ネットワーク機器やネットワークアダプターに付いている固有の識別番号。有線LANのほかに、無線LANやスマートフォンにもMACアドレスが割り当てられている。
>https://www.otsuka-shokai.co.jp/words/mac-Address.html

つまりは, 現実上で機器を識別する番号ですね。
**物理アドレス**っていったりします。

#ポート番号#
>ポートはよく「扉（ドア）」に例えられるます。
IPアドレスが住所「家」であれば、ポートは外に出る・外から入る「扉（ドア）」に該当します。
>https://www.cman.jp/network/term/port/

ドア...なるほど。もう一つ見てみます。

>ポート番号とは、インターネットで標準的に用いられるプロトコル（通信規約）であるTCP/IPにおいて、同じコンピュータ内で動作する複数のソフトウェアのどれが通信するかを指定するための番号。単に「ポート」と略されることもある。
>http://e-words.jp/w/%E3%83%9D%E3%83%BC%E3%83%88%E7%95%AA%E5%8F%B7.html

というわけで, プログラムが通信するときに識別するためにしようする番号ですね。

#プロセスid#
それぞれのプロセスに割り当てられる識別子です。

#何がどう違うのか#
IPアドレス, MACアドレス, ポート番号は**通信**に使用するわけですが何がどう違うのか。ごちゃごちゃになるのでまとめてみます。

##通信での用途##

IPアドレス　　 →　インターネット上で機器を識別するため
MACアドレス　  →  機器同士で通信するため
ポート番号     →　どのプログラムと通信するか

##通信の方法##
宅配で例えてみます。
注文を受けて商品を発送します。そのときの**お届け先の住所がIPアドレス**です。
また運送するときに直接家に届けるのではなく, 色んな物流センターを経由するとします。このときの**物流センターがMACアドレス**です。
さぁ, 商品が無事家に届きました。商品には○○宛てと書いてあります。この**○○宛てというのがポート番号**てきな感じです。

#参考#
[「IPアドレス」と「MACアドレス」の違い](https://wa3.i-3-i.info/diff63address.html)
デプロイする度にアップロードした画像が表示されなくなる問題への対処｜Active Storage

Railsアプリケーションで、デプロイする度にアップロードした画像が表示されなくなる事象が発生したので、その時の解決策を残します。
Capistrano3で自動デプロイをしていて、画像のアップデートにはminimagickを使用。

画像のアップロードに関することなので、Active Storageの設定を修正します。
共有フォルダに画像ファイルを保存し、デプロイ時にデータが上書きされないようにします。

やることは2つだけです。
①Active Storageの画像の保存先を変更し、
②共有フォルダへシンボリックリンクを貼る。

##開発環境
・Rails 5.2.3
 ・Ruby 2.5.1
・ConoHa VPS
 ・CentOS 7.6
 ・Capistrano3
・Nginx
・Unicorn

##①Active Storageの画像保存先の変更

Active Storageの画像の保存先はstorage.ymlで設定します。

保存先がrootディレクトリ直下のstorageだったのを”public/storage”に変更します。

```config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("public/storage") %>  # 元は "storage"
```

これでアップデート先が、/var/www/hoge_app/current/storageから、
/var/www/hoge_app/current/public/storageになりました。

その結果、共有フォルダ（sharedフォルダ）にも画像ファイルが保存されます。
共有フォルダにファイルを入れておくことで、デプロイ時にファイルの上書きを避けることができます。

##②共有フォルダへシンボリックリンクを貼る
capistranoでデプロイをしているので、capistranoの設定を修正します。

画像ファイルが保存されている共有フォルダへ、デプロイ時にシンボリックリンクを貼る設定をします。

```config/deproy.rb
 append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "vendor/bundle", "public/system", "public/storage" # “public/storage"を加える
```

これでデプロイ時に/var/www/hoge_app/shared/public/storageへのシンボリックリンクが貼られます。

これで設定は完了です。
この設定にしてからアップロードした画像は、デプロイ後もきちんと表示されました。

##参考
https://railsguides.jp/active_storage_overview.html
https://lab.unicast.ne.jp/2015/07/02/capistrano3%E3%81%A7%E3%81%AFshared_children%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8Flinked_dir%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82/
https://qiita.com/okjnag/items/ee55fd0f214466a4d3c8
https://qiita.com/hmmrjn/items/7cc5e5348755c517458a#active-storage-%E3%81%A8%E3%81%AF
Automation Anywhere A2019の「コメント」と「ステップ」で見やすいフローを作ろう

Automation Anywhere A2019で多くの段階のフローチャートを作っていく際には、フロー自体の可読性や他のプロジェクトメンバーへの情報提供の配慮も行いたいところです。この記事では、フロー作成の際に情報整理をするためのテクニックを説明します。

**利用環境**
Automation Anywhere A2019 Community Edition ビルド1598

# 「ステップ」を使って一連のアクションをひとまとめにする
アクションをたくさん追加していったときに、「ここはRecorderで画面操作するアクションのブロックだな」とか「ここはファイル入出力を取り扱う部分だな」とか、一連の操作をしている箇所がある場合、「ステップ」(Step)というアクションを挿入してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c98f3c9f-e204-ace4-8b38-8a5c8e018dd0.png)

そうすると、以下のような枠付きのStepアクションが挿入されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/8bf1b7fd-7149-7366-dc10-ef6bce2e9795.png)

整理したいアクションを枠の中にドラッグ＆ドロップするとひとまとまりにすることができます。
アクションの詳細でタイトルを書くと、コメントとして反映されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/01950dd3-7a80-9f88-df02-2eca1fe0ada9.png)

ドラッグ＆ドロップしてみるとこんな感じです。
このStepアクション自体ではロボット実行時には何も行われません。単純に見た目を整理するためのアクションです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/a23dd4b4-d37c-8463-6e33-aa73a06f1e54.png)

Stepアクションのアイコンを選択すると、[…]と[▶]の2つのアイコンが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/44417565-c9a5-509c-1c67-99e0c0d2ad00.png)

[…]はクリックすると他のアクションと同様にコンテキストメニューが表示されますが、[▶]をクリックすると、囲われているアクションリストをたたむことができます。これでだいぶ見やすくなりましたね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/bef26434-ffd5-4318-f4d7-4ab3491855da.png)

ちなみに、Stepは多重にすることも可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/8e5ef4ed-55ff-078e-57f4-c586fc952063.png)

「ループ」アクションもStepと同様に使うことができます。(タイトルは入れられませんが)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/a1748eee-19bd-9e32-9f6f-2f327df9ad78.png)



# 「コメント」を使って可読性を上げる
Stepアクションで囲うほどのアクションがない場合、「コメント」アクションを活用することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/abbcc4ba-130e-542b-7f71-191a68e5d322.png)
「コメント」(Comment)アクションをドラッグ＆ドロップで挿入すると以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/150f6b4a-7ba4-69c6-2a98-6bc56660e68b.png)

実際にコメントが入るとこんな感じです。
このCommentアクション自体もロボット実行時には何も行われません。単純に見た目を整理するためのアクションです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/bc420721-f96e-a5d5-ffd7-f57c89fddfda.png)

コメントアクションはフローの中で通常のアクションと同じ幅を取ってしまうので、個人的にはちょっと邪魔な気もします。Stepアクションのほうが折りたたみできる分、見た目にはしっくりくるのかもしれません。

# リスト表示で見た場合
いままではフロー表示でどう表示されるかを見ましたが、リスト表示での見え方も見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/cf9caaf2-eb6e-2a93-c875-af5058aab3fe.png)

アクションの数が多い場合は、リスト表示の方がすっきりくるでしょう。
ちなみに、「Step」や「ループ」は折りたたむことができます。Commentは折りたたむことはできません。

折りたたむとこんな感じによりスッキリしました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/500c1f10-72c5-8fef-e6a6-9fb3a194539d.png)

最後までお読みいただきありがとうございました。