OmniAICreator/Qiita-1.07M huggingface dataset:
Ansible初心者 証明書のCSR作成

#0. はじめに
Ansibleで証明書のCSRを作成する。
準備として、作業ディレクトリの作成とユーザ/グループの作成を実施。

```
- name: Create work directory
  file:
    state: directory
    path: "{{ tmp_dir }}"
```

#1. 秘密鍵の作成
openssl_privatekeyモジュールを利用して秘密鍵を作成。

```
- name: Generate OpenSSL private key.
  openssl_privatekey:
    path: "{{ tmp_dir }}/{{ private_key_name }}.key"
    type: "{{ private_key_type }}"
    size: "{{ private_key_size }}"
    owner: "{{ private_key_owner }}"
    group: "{{ private_key_group }}"
    mode: '0600'
```

変数を設定するvarsファイルは以下の通り。

```
private_key_name: "test"
private_key_type: "RSA"
private_key_size: "2048"
private_key_owner: "apache"
private_key_group: "apache"
```

#2. CSRの作成
openssl_csrモジュールを利用してCSRを作成。

```
- name: Generate an OpenSSL certificate signing request.
  openssl_csr:
    path: "{{ tmp_dir }}/{{ csr_common_name }}.csr"
    privatekey_path: "{{ tmp_dir }}/{{ private_key_name }}.key"
    country_name: "{{ csr_country_name }}"
    state_or_province_name: "{{ csr_state_or_province_name }}"
    locality_name: "{{ csr_locality_name }}"
    organization_name: "{{ csr_organization_name }}"
    organizational_unit_name: "{{ csr_organizational_unit_name }}"
    common_name: "{{ csr_common_name }}"
    owner: "{{ csr_owner }}"
    group: "{{ csr_group }}"
    mode: '0600'
```

変数を設定するvarsファイルは以下の通り。

```
csr_country_name: "JP"
csr_state_or_province_name: "Tokyo"
csr_locality_name: "Chuo-ku"
csr_organization_name: "XXX"
csr_organizational_unit_name: "xxx"
csr_common_name: "test"
csr_owner: "apache"
csr_group: "apache"
```

#3. 実行結果確認
管理対象サーバを確認し、秘密鍵とCSRが作成されていることを確認

```
-rw-------. 1 apache apache 1675  9月 24 22:57 test.key
-rw-------. 1 apache apache 1033  9月 24 22:57 test.csr
```

#4. おわりに
今回はcsrの作成を実施したが、次回は自己証明書を作成しHTTPS通信を行う。
ご参考：[自己証明書の作成](https://qiita.com/say8/items/22871d8f207b7d4b43cb)
GCPとAWS どっちがいい

#重要なこと
僕は、インフラメインの人間ではないです。
ポエムだと思ってください

#主観の感想
インフラ専門の方はAWSを使われるイメージが強いです
今までの知見とかが溜まっているとかがあるんでしょうか
自分の周りは会社ではAWSを使っているとよく聞く気がします
でも最近GCP勢が増えてきてる気もします
kubernetesとか機械学習が影響しているんでしょうか

個人的にはドキュメントと操作性でGCPのが使いやすく感じました
あとCIとの連携もGCPのがやりやすかったです
慣れの問題だと思いますが、、

特に機械学習をやるときにSageServerとTensorflow周りが使いにくかった印象です
料金面の優越は正直分かりませんが、ある程度のアクセス数が無い限りはどっちでもいいのではと思える額でした。
個人開発で使う分ではGCPの永久無料枠と最初の3万円無料は大きかったです

GCPのai-platformはかなり安いので、colabとか使ってる人は試しに使ってみてもいいかもしれません

僕は最初AWSだったんですけど、機械学習があってGCPに乗り換えました
【Rails】ActionMailerを使用してSendGridのAPI経由でメール送信する場合の差出人名

## 概要
メールを送信するときに差出人名を表示させたいことがあると思います。検索すると、[Action Mailerでfromフィールドに差出人名を表示したい](http://kotaroito.hatenablog.com/entry/2016/09/23/103436)に書いている通り、fromに差出人名を文字列で入れれば反映されるのですが、SendGridのAPI経由で送る場合はこの方法では反映されないです。というわけでどうすれば良いのかというのをメモ。

## 対応
SendGridでは専用のメールアドレス設定用クラス（Email）が用意されていて、[Send sender name with email SendGrid - Rails](https://stackoverflow.com/questions/51648171/send-sender-name-with-email-sendgrid-rails)にある通り、アドレスと差出人名を設定する仕様になっています。

## 実装サンプル
ActionMailerでのSendGrid・API使用について、[Rails: SendGrid(Web API)とAction Mailerでメールを送信する](https://techracho.bpsinc.jp/yamada/2018_12_22/67222)の記事がわかりやすくまとまっているのでこちらを元にします。
設定箇所は`lib/mail/send_grid.rb`のfromの部分になります。下記のような形で2つ目の引数に、差出人名を設定します。（差出人名は固定の前提）

```send_grid.rb
 from = SendGrid::Email.new(email: mail.from.first, name: '山田太郎')
```
[PHP]PDOでDBへ接続する時に便利なクラス

PHP開発をしていると、絶対にデータベースに接続することは一度はあるはずです。今回はそんなDBへの接続をお手軽にするメソッドを紹介します。

#PDOとは
PDOとは、PHP Data Objectの略称で、ナウいPHPではこのクラスを使ってデータベースに接続しています。PDOを正しく使っていればある程度SQLインジェクション対策にもなるため、セキュリティ観点からもWEB上に公開するアプリケーションはこのPDOを使うことが推奨されます（多分）。

#DBへの接続を簡素にするコード

```php:setting.php
define("DEV", $_SERVER['SERVER_NAME'] === "localhost" ? true : false); //開発環境か否か
$_ = function(string $str): string{return $str;}; //ヒアドキュメント用

class Connect
{
    //三項演算子を使って開発環境と本番環境でDSNを出し分ける
    const HOST = DEV ? "localhost" : "xxx.example.net";
    const USER = DEV ? "root" : "server_admin_name";
    const PASS = DEV ? "root" : "server_password";
    const UTF = "utf8";

    public function __construct($dbName)
    {
        global $_;
        $dsn = "mysql:dbname={$dbName};host={$_(self::HOST)};charset={$_(self::UTF)}";
        try{
          $pdo = new PDO($dsn, self::USER, self::PASS, [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$_(self::UTF)}"]);
        }catch(Exception $e){
          echo "Connection Error";
          die();
        }
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
        $this->dbh = $pdo;
    }
}
```

実際に接続して、値を取得するときは以下のようにする

```php
$con = new Connect("database_name");
$pdo = $con->dbh;
//プリペアドステートメントなしで値を取得する
$stmt = $pdo->query("SELECT id, name FROM TABLE1;");
while($item = $stmt->fetch(PDO::FETCH_ASSOC)){
    echo "id:{$item['id']} name:{$item['name']}";
}

//IDが$xのnameを取得する場合
$stmt = $pdo->prepare("SELECT name FROM TABLE1 WHERE id = ? LIMIT 1;");
$stmt->execute([$x]);
$item = $stmt->fetch(PDO::FETCH_ASSOC);
echo "idが{$x}のnameは{$item['name']}です。";
```
CI 比較

趣味も仕事も結構CI使ってて、プライベートでは色々自動化しました。会社で使う分には、あんまり料金とか気にならないですが、趣味で使う分には参考になるかもしれません。
触ったことあるやつとか、無いやつとかまとめました

# CI
* Jenkins
* CircleCI
* GithubActions
* TravisCI
* (CloudBuild CloudRun)

#自分の主観です

###Jenkins
付き合いが長すぎて、飽きましたが、一番汎用性が高いです。むしろコンテナ的使い方というより、cronで済むようなことをJenkinsでやっていました。
EC2とかに入れてコンテナ使わずにvirtualenvとかで動かす分には、これが一番安く使えました。
長い時間パースしたりする作業とか、頻繁にコンテナ立ち上げる作業だと結構費用がかさむので、そういう系は今でもJenkinsでやってます。

###CircleCI
Xcodeのビルドができるんですよ。これ凄く大きいんですけど、XcodeのビルドはJenkinsでやってたんで、使ったことないです。
無料枠がそんなに大きくなかったのと、UIが新しいサイトと古いサイトで行ったり来たりしてちょっと使い辛かったです。
無料枠とUIが原因で結局GithubActionsに移動しました

###GithubActions
プライベートリポジトリでも結構な無料枠があるんですよね。
ジョブごとにyamlファイルを分けれたりするので、すっきりします。
yamlがCircleCIと似てるので、キャッチアップが楽でした。
主観ですけど、UIが綺麗です。
私は、今ほとんどGithubActionsでやってます。
ただ、コンテナをプルしてきて実行して、成果物をGCSにアップロードしてってやってたらGCPの値段が結構かかってしまいました。
結局EC2のJenkinsで回して、たまーに成果物をまとめてGCSにアップロードするのが一番安くつくので、そうしてます

###Travis
オープンソースとかだと、TravisCIをめっちゃ見かけるけど、僕は使ったことないです
そもそも、ソースコードがオープンじゃないので、使ってませんでした。

###CLOUD BUILDとCLOUD RUN
GCPのデプロイ関連だとGCPで完結したいと思ったんですが、ドキュメントがなんか分かりにくかったので私は諦めました。
あと、テストを回したりlintチェックしたりSlackに通知したりする場合どうするんだろうっていうのもありました。
Ruby on Rails 〜MVCとルーターの基礎〜

#はじめに
本記事では、MVC（Model（モデル）、View(ビュー),Controller(コントローラー)とRouter（ルーター）の関連を確認しながら、用語について解説していきます。

#Router（ルーター）とは
RouterはHTTPリクエストがあったURLに対して、アプリケーション内のどの機能を結びつけるを決める役割を果たします。そして次に説明するControllerに１つのメソッドを割り当てます。

#Controller(コントローラー）
"Controller"はMVCパターンの司令塔的役割を果たします。
これは、ユーザから送られてきたHTTPリクエストの処理を担当します。そのHTTPリクエストはRouterからControllerの１つメソッドと結びつきます。
そしてControllerはHTTPリクエストに対応したModelの作成や取得、取得した値をViewに渡して処理を行わせる、というようなことを行います。
つまりModelやViewに命令を出すことがControllerの仕事です。

#Model(モデル）
"Model"はデータとやりとりをしてくれる存在です。
Controllerからの命令に従い、データベースから情報を取得したり、データを更新したり、書き込んだり、データを消去したりします。
データベースと直接やりとりできるのはModelだけです。

#View(ビュー）
ViewではControllerから渡されたデータを、HTMLページでどのように表示するかを定義します。最終的にHTTPレスポンスとして返すWebページです。
Viewでは見た目の部分だけでなく、Controllerによって選択された適切なModelが埋め込まれます。

#まとめ
これを一連の流れにしてまとめます。
RouterはHTTPリクエストを受け取り、適切なアクションとひも付けます。
Controllerはそのアクションを実行してModelやVeiwに指示を出して、ViewをHTTPレスポンスとして返すと行ったところでしょうか。
TypeScript入門　その3 enum、タプル、関数オブジェクト

[こちらのブログ](https://yk0807.com/techblog/2020/09/24/typescript%e5%85%a5%e9%96%80%e3%80%80%e3%81%9d%e3%81%ae3/)と同じ内容です。

TypeScript入門3回目の今回は、データ型の中でも、前回紹介できなかった列挙型（enum）、タプルや関数オブジェクトあたりについて説明しようと思います。

# 1. 列挙型（enum）
**列挙型（enum）**とは、あらかじめ決められた値のみを取ることができるデータ型です。例えば、こんな感じです。

```TypeScript
enum Signal {
  Red,
  Yellow,
  Blue
};
let red = Signal.Red;
let yellow = Signal.Yellow;
let blue = Signal.Blue;
console.log(red); // 0
console.log(yellow); // 1
console.log(blue); // 2
// let green = Signal.Green // コンパイルエラー！
```

このようにして定義された変数signalは、Red, Yellow, Blueのいずれかしか代入することが出来なくなっています。
また、enumはデフォルトでは内部では0から始まる整数を取るようになっていますが、

```TypeScript
enum Signal {
  Red = 2,
  Yellow = 3,
  Blue = 4
};
```

というように、定義するときに取る値を変更することもできます（数値だけでなく、文字列を入れたりもできます）。
ところで、最近では[enumを使わない方がいいという意見](https://www.kabuku.co.jp/developers/good-bye-typescript-enum)もあるようです。
enumを使わない書き方としては、こんな感じです。

```TypeScript
const Signal = { 
  Red: 0,
  Yellow: 1,
  Blue: 2
} as const;

type Signal = typeof Signal[keyof typeof Signal];
let red = Signal.Red;
console.log(red);  // 0
```

このような有限集合のデータ型を**Union型**と言います。
TypeScriptの経験が浅い自分はenumのデメリットを把握しきれてないのですが、enumはJavaScriptとの互換性がないというのは確かにありますね（コンパイル後のソースを見ると一目瞭然）。

#2. タプル
次は、複数の型の値をまとめて持つことができるタプルというものを紹介します。
配列のように複数のデータ型を宣言することで定義できます。
例えば、こんな感じです。

```TypeScript
let person: [string, number, string];
person = ["Taro", 26, "Tokyo"];
console.log(person[0]); // Taro
console.log(person[1]); // 26
console.log(person[2]); // Tokyo
person[1] = 30; // OK
person[1] = "30"; // コンパイルエラー
```

とはいえ、この書き方だと何を表しているかわからなくなりがちなので、下記のような使い方の方がわかりやすいかもです。後、先ほどのenumももちろん要素にすることができます。

```TypeScript
type name = string;
type age = number;
type address = string;
enum gender {male, female};

let person: [name, age, address, gender];
person = ["Taro", 26, "Tokyo", gender.male];
```

#3. 関数オブジェクト

TypeScriptでも、JavaScriptと同じように関数の定義を行えますが、やはりそこはTypeScript。引数と戻り値に型を指定することができます。

```TypeScript
function multiple(a:number, b:number):number {
  return a * b;
}
multiple(5, 3); // 15
multiple(5, "3"); // コンパイルエラー
```

また、TypeScriptでは下記のようにオプション引数を使うことができます。
```TypeScript
function multiple(a:number, b:number, c?:number):number {
  if (c) {
    return a * b * c;
  }
  return a * b;
}
multiple(5, 3); // 15
multiple(5, 3, 4); // 60
```

後は、同じ名前の違う型で定義された関数も定義することができます。いわゆるオーバーロードです。

```TypeScript
function add(a:string, b:string):string;
function add(a:number, b:number):number;

function add(a:any, b:any):any {
    if (typeof a === "string" && typeof b === "string") {
        return a + ":" + b;
    }
    return a + b;
}
add("Hello", "World"); // Hello:World
add(7, 4); // 11
add("Hello", 4); // コンパイルエラー
```

今日はこの辺りで。
次回最終回（予定）はクラスとかのオブジェクト指向に関する話題を紹介しようと思います。

[その１](https://qiita.com/yk0807/items/d8b6abaaa8e999e400fd)
[その２](https://qiita.com/yk0807/items/a5648e41d27d22ddeac2)
【Rails】ページの上部に戻るボタン

##　はじめに
・Railsを使用してアプリケーションを開発
・viewsの下部地点から上部に戻るボタンを設置する

Ruby 2.5.7
Rails 5.2.4.4


## 流れ
・```gem 'jquery-rails'``` を導入
・アクションを起こすボタンを用意（画像でなく文字やfont-awesome等でも代用可能）
・viewにボタンを記載しcss及びjavascriptへ追記

## ボタン画像
![top_btn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705913/7903465d-ed58-f189-fab1-45198f70b9b6.png)

適当に準備します・・・

画像ファイルをapp/assets/images配下に格納

## viewsへ追加
```ruby

<span id="top-btn">
  <a href="#">
    <%= image_tag asset_path('top_btn.png'), class: 'top-btn' %>
  </a>
</span>

```
・asset_path('~~') は、app/assets/images配下の画像を読み込む

## css 及び　jsファイルへ追記
```css
.top-btn {
  position:   fixed;
  right:      30px;
  bottom:     40px;
}

.top-btn a {
  width:      50px;
  height:     50px;
}
```

``` javascript

$(function() {
  $('#top-btn a').on('click',function(event){
    $('body, html').animate({
      scrollTop:0
    }, 1000);
    event.preventDefault();
  });
});

```

## 初期状態(ページの上部)では表示したくない場合?
``` javascript
$(function() {
	var topBtn = $('#top-btn a');
	topBtn.hide();
	$(window).scroll(function(){
		 if ($(this).scrollTop() > 100) {
			 topBtn.fadeIn(1000);
		 } else {
			 topBtn.fadeOut();
		 }
	});
	topBtn.click(function () {
		$('body, html').animate({
      scrollTop:0
    }, 1000);
    event.preventDefault();
  });
});
```
fadeIn 及びfadeOut のif文にてボタンの表示を設定しています・・

## 終わり
上記記載内容で、実装できるはず・・・
不明点及び説明がずれている（誤っている等）ございましたら、コメントにてにてお伝えいただければありがたいです。

以上、ご参考になれば幸いです。
Laravel  簡単なページネーション機能を作成

Laravelには、とても便利なページネーション機能があります。
今回はEloquentモデルで実装してみたいと思います。


やり方
1 コントローラーに以下を追加する
```$users = User::paginate(10);
```

2 viewに$usersを渡してあげて表示する


3 ページ番号の前と後の矢印を表示するために、 
viewに ```{{ $users -> links(); }}``` を書いてあげる


これだけでページネーション機能を作れるので楽ですね。







参考サイト; https://readouble.com/laravel/5.3/ja/pagination.html
Windows10にWSL2をインストールするときの注意

# はじめに
DockerをWSL2で動かしたくて、Windows10にWSL2をインストールしようとして、ずっぽりハマったので、次にハマる人のための備忘録。説明ページの問題ですでに[認識されている](https://github.com/MicrosoftDocs/WSL.ja-JP/issues/18)ので、近いうちに直ると思われます。※ 記事を書いているのは、2020年9月24日。
ただこのトラブルは他でも起こりうるし、そのときこの回避方法は有効かもしれないので、記録として無駄にはなるまい。

先に原因と対策を書くと、自動翻訳でミスっていて、日本語のページのみでリンクが不正だったということでした。URLのja-jpをen-USにすると、解が得られます。


# 事象
インストールにあたり、下記のURLを参考にしていました。
Windows 10 用 Windows Subsystem for Linux のインストール ガイド
https://docs.microsoft.com/ja-jp/windows/wsl/install-win10
↑あえてフルのURL

|![20200924_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/ccb8732e-362b-247d-7ed4-97c83241490f.png)|
|:-:|

ずずっと進めると、ここでエラーです。

|![20200924_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/d6b41b9d-58e2-ded9-ac65-b65b52bd15e1.png)|
|:-:|

でもそれは想定通りとのことです。
実行したコマンドは `wsl --set-default-version 2`で、エラーメッセージは `WSL 2 requires an update to its kernel component. For information please visit https://aka.ms/wsl2kernel` の日本語だったので、書いてある文章の翻訳がおかしいなと気づくのですが、そこは大らかな気持ちで、https://aka.ms/wsl2kernel をクリックしてみました。
そうすると飛ばされるページは今見ているページ（ https://docs.microsoft.com/ja-jp/windows/wsl/install-win10 ）で、無限ループに陥るということです。

# ググると・・・
この問題をググると、みなさんMSIをダウンロードできているようで、参考になりませんでした。

このあたりから、翻訳が怪しい？オリジナルの記事を見たい！と思ったけど、ページの上の方に[ja]とか[日本語]みたいなリンクがないので、あきらめました。（URLをいじるところまであと一息だったのに力不足感...）

# 解決までの道

ページの下まで見ると、フィードバックでメッセージが送れそうだと気づき、さらにgithubの猫？githubで管理されてるのか・・・。

|![20200924_3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/1eb40254-f9ea-eabc-8eb5-a04ca3908f2b.png)|
|:-:|
この猫を開いてメッセージ入力しようとしたら、Issueが登録されていてプルリクが出てる。

|![20200924_4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/413b11f8-780b-1de3-e51c-04b4e057cafc.png)|
|:-:|
同じ問題が登録されているかも？と思ってみてみると

|![20200924_5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/44858ae8-7b69-079e-0c08-618782a7b50c.png)|
|:-:|
ドンピシャじゃないですかー！tdtdsさん！5日前！
あとは辿って行ったらゴールが見えましたとさ。

|![20200924_6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/3d681457-9395-1188-ea1c-c3fcef148907.png)|
|:-:|

# 得られた教訓

困ったときは、オリジナルの英語版も見てみること。
オリジナルへの正式なリンクがなくても、URLをいじれば見れることが多い。

以上ですー
YouTube Data APIのクォータ数増加申請が通った件の振り返り

申請が通って増えたので振り返り。

## 概要
YouTube Data APIは1日に利用できる回数(クォータ、Quota)が決まっている。
APIキーの初回発行時には10,000クォータ。
利用するAPI毎に消費するクォータが決まっていて、検索(search)なら1回100Quota。Channel情報取得(channels)なら1回1Quota。
機能がかなり制限されてしまうので、増加の申請を行った。

## 申請内容ざっくりと引っかかった項目

[申請フォーム](https://support.google.com/youtube/contact/yt_api_form?hl=en)にて必要事項を記入し申請した。
日本語でのフォームがないので、各項目毎にGoogle翻訳をかけ、回答も英語で記入した。
Quota増加の申請なので最初の項目は「Quota Extension Request」と記入。

- Your organization's website *
    - httpではダメ、httpsは必須(YouTube APIの利用規約にもある)。
- Send documents (e.g., design documents, etc.) relating to your implementation, access, integration and use of YouTube API Services by each of your API Client/If the API Client is not accessible publicly, please upload a detailed screencast of each YouTube API Service functionality and YouTube data, content and information as displayed on, or integrated into, this API Client. *
    - 私の場合は、APIクライアントがバックで収集するタイプだったので、クライアントは公開できなかった。
    - そのため、VSCodeでAPIを使用しているソースのスクリーンショットを4枚ほど作成しzip圧縮して送付した。
- How much "Additional Quota" are you requesting? *
    - 欲を出して50万程度で申請した。
    - そこまでとはいかないが、そこそこの量を頂けたので満足している。
- Justification for requesting additional quota? *
    - 収集するチャンネル情報とスケジュール情報を増やしたいから、とした。
- Explain in detail how you use YouTube API Services today *
    - チャンネル情報や動画情報を収集し、一覧表示したい、とした。
- What functionality would your API client be lacking without more quota? *
    - チャンネル数やスケジュール情報の収集回数が減り、リアルタイム性が下がる、とした。
- What potential workarounds would you use to compensate for less quota? (ex. decreased feature set, estimations, smaller sampling) *
    - チャンネル数や収集回数を減らす対応を行っている、とした。

## 申請後の動き
### 2020年9月11日
申請

### 2020年9月14日
メールにて1次回答があり、添付のPDFファイルに記載されている指摘事項への対応を15日以内にお願いします、とのこと。
PDFの内容はざっくりと以下。

1. 申請したAPIクライアントで使用しているプロジェクトIDを再度教えて欲しい。複数のプロジェクトで使用している場合はそれも同様に。
1. YouTube APIのサービス利用規約の参照(リンク)が存在しない。
1. 常に閲覧可能な箇所にプライバシーポリシーを作成すること。
1. 申請されたURLにhttpsでアクセスするとセキュアでないと警告が出るがこれは規約違反である。

### 2020年9月15日
以下対応を行いメールで回答を送付した。

1. プロジェクトIDをメールで回答した。
1. プライバシーポリシーページを作成し利用規約に関する記述・リンクを追加した。
1. 同上
1. 単純なミス。SSL証明書として「lisvir.jp」のみ設定していたが「holo.lisvir.jp」も追加

### 2020年9月16日
これから審査に入る、とメールで回答があった。

### 2020年9月22日
クォータ数を増やしました、とメールで回答があった。


という流れでした。
ディレクトリ内をlsで一覧出してcatしてリダイレクトしたら無限ループになった

頭悪いタイトルですみません。笑

# 現象
以下のようなディレクトリにて、以下コマンドを実行すると無限ループに陥り、強制終了すると巨大なファイルが生成されていた。

```
ls | xargs cat | grep target > out.txt
```

``` :ディレクトリ
dir/
   bigtext01.txt
   bigtext02.txt
   bigtext03.txt
   bigtext04.txt
   ...
   bigtext99.txt
```

# 解決方法
ls で ignore オプション(-I) 付与する
リダイレクトによる出力ファイルを ls してしまうと無限ループに陥り、どんどんファイル容量が肥大化していく（危険！！！）ので出力ファイルをlsで読まないようにしておく。

```
ls -I out.txt | xargs cat | grep target > out.txt
```

# 原因（推測）
各コマンドのソースコードは確認していない。現象からの推測。
ファイル容量がある程度大きい場合に、すべてのコマンドが処理し終える途中で、リダイレクトによるファイルが生成される。そのため、その生成ファイルをlsがピックアップし、コマンドの処理を行う...という再帰的な処理に陥ってしまう。

# きちんと原因調査
またいつかやります

以下のコマンドは無限ループにならないので最初は原因が分からずとても困った。

```
ls | xargs cat | grep target
```
【Git】git stash 加えた変更をcommitせずに避難させたいとき

駆け出しの新人エンジニアですが、それにしても頻繁にgit stashというコマンドを使用することが多いのでメモのような形でここに残しておきます。

## そもそもgit stashとは
ローカルで作業を行っている際、加えた変更をそのままどこかに一時的に保存しておきたいときに使えるコマンドです。そして、この一時的にどこかに保存した変更はもちろん元に戻すことができます。


### 変更を避難させる
```
$ git stash -u
```
これはよく僕が使っているコマンド。
-u　これはオプションだが、``追跡されていないファイルもすべて避難させる``という意味があるらしい。
Vscodeでうまくstashできない時があり、-uを付けたら解決したので頻繁に使っている。

###　避難させた変更を元に戻す
```
$ git stash apply
```
これで避難させた変更を再び蘇らせる。stash listにはそのまま残るらしい。
 
### stashした履歴？を確認する
```
$ git stash list
```
これをするとリストで避難させた履歴を確認することができる。
applyはこのリストの一番最新のものを蘇らせることができるコマンド。

##　まとめ
git stashは奥が深そう。

特に駆け出しの時に、周りの優秀なエンジニアの方が次々とmasterにmergeしていっているときとかに使えると思うので、新人のうちからでも覚えておくと助かるコマンドかもしれない。
cron

# cron とは
> cronとは、多くのUNIX系OSで標準的に利用される常駐プログラム（デーモン）の一種で、利用者の設定したスケジュールに従って指定されたプログラムを定期的に起動してくれるもの。
> 引用：[IT用語辞典 e-Words](http://e-words.jp/w/cron.html)

# cron を起動（停止）する
cron を利用するにはデーモンを起動する必要があります。
```$ sudo service cron start
```

停止
```$ sudo service cron stop
```

起動確認
```$ sudo service cron status
```

# ファイルの配置
/etc/cron.d/ にある拡張子のないファイルが実行されます。

```bash
sudo vi /etc/cron.d/your-script
```

# 書式
```[分] [時] [日] [月] [曜日][実行するユーザ][コマンド]
```

- 分: 0～59
- 時: 0～23
- 日: 1～31
- 月: 1～12
- 曜日: 0 or 7 (日)、1 (月)、2 (火)、3 (水)、4 (木)、5 (金)、6 (土)
- コマンド: PATH を通していないものは絶対パスか相対パスで指定

# XX時YY分に実行

```vim:your-script
SHELL=/bin/bash
# 毎日 09:05 にスクリプトを実行
5 9 * * * ubuntu SCRIPT
```

# X 分間隔で実行

```vim:your-script
SHELL=/bin/bash
# 1 分間隔でスクリプトを実行する
*/1 * * * * ubuntu SCRIPT
```


# X 秒間隔で実行
書式の範囲では表記できません。最小単位が ”分” だから。

```vim:your-script
SHELL=/bin/bash
# 5 秒間隔でスクリプトを実行する
* * * * * ubuntu for i in `seq 0 5 59`;do (sleep ${i} ; SCRIPT) & done;
# 15 秒間隔でスクリプトを実行する
* * * * * ubuntu for i in `seq 0 15 59`;do (sleep ${i} ; SCRIPT) & done;
```

# 日時指定いろいろ
| [分] [時] [日] [月] [曜日] | 実行日時 |
|:-:|:-:|
| 5 10 * * * | 10:05 |
| 0 9 * * * | 9:00 |
| 0 9 * * 1 | 月曜日 9:00 |
| 0,30 9 * * 1,2 | 月・火曜日の 9:00 と 9:30 |
| 0-10 9 * * * | 9:00 から 9:10 で 1 分間隔  |
| 0,10,20,30,40,50 * * * * | 10 分間隔  |
| */10 * * * * | 10 分間隔 |
| * 9 * * * | 9:00 から 9:59 まで 1 分間隔 |
| 0 * * * * | 00 分に 1 時間間隔 |
| 15 1-12/2 * * * | 1:15, 3:15, 5:15, 7:15, 9:15, 11:15 |


# ログ設定 （デフォルト無効）
```$ sudo vi /etc/rsyslog.d/50-default.conf
```

``` 50-default.conf
- ＃cron.*   /var/log/cron.log
+ cron.*    /var/log/cron.log
```
# sudo tree /etc/cron*
```bash
ubuntu:~$ sudo tree /etc/cron*
/etc/cron.d
├── e2scrub_all
└── popularity-contest
/etc/cron.daily
├── apport
├── apt-compat
├── bsdmainutils
├── dpkg
├── logrotate
├── man-db
├── popularity-contest
└── update-notifier-common
/etc/cron.hourly
/etc/cron.monthly
/etc/cron.weekly
├── man-db
└── update-notifier-common
/etc/crontab [error opening dir]

0 directories, 12 files
```

# 環境
```bash
ubuntu:~$ uname -a
Linux ubuntu 5.4.0-48-generic #52-Ubuntu SMP Thu Sep 10 10:58:49 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
ubuntu:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.1 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.1 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
```
【Auth0】ログイン画面を日本語化する

# はじめに
Auth0のUniversal LoginにはClassicとNewの二つがありますがどちらもデフォルトだと英語が表示されます。

Classicの場合
![スクリーンショット 2020-09-24 17.36.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/9d5a17b8-16c1-9d89-1bc3-3fe4df31b1cd.png)

Newの場合
![スクリーンショット 2020-09-24 17.40.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/82beaac6-efc3-36c1-2f3d-7103effcee1b.png)


今回はこれら二つのログイン画面の表記を日本語にしてみました。

# Classicの場合

Universal LoginのCustomize Login PageをONにします。

![スクリーンショット 2020-09-24 18.37.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/571ced99-2808-3ceb-8a00-2a6469e946b8.png)

すると、画面下部にhtmlを編集する画面が表示されるので、変数の`language`に`'ja'`を設定します。

```html
<!DOCTYPE html>
<html>
<head>
  ...
</head>
<body>
  ...
  <script>
    // Decode utf8 characters properly
    var config = JSON.parse(decodeURIComponent(escape(window.atob('@@config@@'))));
    config.extraParams = config.extraParams || {};
    var connection = config.connection;
    var prompt = config.prompt;
    var languageDictionary;
    var language = 'ja'; // 日本語化

    if (config.dict && config.dict.signin && config.dict.signin.title) {
      languageDictionary = { title: config.dict.signin.title };
    } else if (typeof config.dict === 'string') {
      language = config.dict;
    }
    var loginHint = config.extraParams.login_hint;
    var colors = config.colors || {};
  ...
  </script>
</body>
</html>
```

これでClassic Universal Loginの画面が日本語化されます。
![スクリーンショット 2020-09-24 18.48.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/c743fe34-71a5-ae3a-6fa9-ca19cc144a1f.png)


# Newの場合
まず、Auth0の管理画面の右上に表示されている自分自身のアイコンをクリックしてテナントの設定画面を開きます。
テナント設定の`General`タブの下のほうにある`Languages`設定内の`Supported Languages`にある`Japanese(ja)`にチェックを入れます。

![スクリーンショット 2020-09-24 18.55.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/1df49340-708f-69d0-1a8e-c055f078b231.png)

ここで`Japanese(ja)`にチェックを入れておけば、パラメータ（クエリパラメータまたはリクエストヘッダ）を渡すとNew Universal Loginが日本語で表示されます。

パラメータの優先順は以下のようになっています。

1. `/authorize`にアクセスする時のクエリパラメータに`ui_locales`を設定(例：`ui_locales=ja`)
2. ブラウザが設定する`Accept-Language`リクエストヘッダの設定

これら二つのパラメータの値が、上記`Supported Languages`でチェックを入れたものの中に含まれていれば該当した言語でログイン画面が表示されるという仕組みのようです。

逆にパラメータの値が`Supported Languages`の対象の中から見つからなかった場合は`Default Language`で設定されている言語が表示されます。
念のため、日本語でログイン画面を表示したい場合は以下のように`Default Language`も`Japanese(ja)`に設定しておくことをお勧めします。

![スクリーンショット 2020-09-24 19.13.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/c1751375-9692-3dab-8bff-fe0848a5da10.png)

無事日本語化されると以下のように表示されます。
![スクリーンショット 2020-09-24 19.17.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653972/3f6a0923-0a50-e4d9-aaf2-9da85d8c0892.png)


# おわりに
Auth0を利用するにあたって一番はじめに設定するような基本的なことではありますが
こういう点もちゃんと対応しておきたいですね。
Go言語向けbash completionを使いたいという願望を叶える方法

Go言語のコンパイラ、インタプリタ実行時にコマンドがTAB補完出来ず不便な思いをしていました。探したところ、`gocomplete`と言う拡張機能をインストールするとbash completionの様にTAB補完出来ることが分かりました。故に、利用方法を備忘録メモとして残しておこうと思います。

# 拡張機能の入手

```bash
$ go get -u github.com/posener/complete/gocomplete
```

## 2022/10/3更新
Go1.16.xからは`go install`コマンドを用いるようになりました．(@バージョンを必ず付与。@latestで最新版をインストール)

```bash
go install github.com/posener/complete/v2/gocomplete@latest
```

<details><summary>実行結果</summary><div>

```bash
$ go install github.com/posener/complete/v2/gocomplete@latest
go: downloading github.com/posener/complete v1.2.3
go: downloading github.com/posener/complete/v2 v2.0.1-alpha.13
go: downloading github.com/hashicorp/go-multierror v1.0.0
go: downloading github.com/posener/script v1.1.5
go: downloading github.com/hashicorp/errwrap v1.0.0
```

</div></details>

* 参考

https://qiita.com/eihigh/items/9fe52804610a8c4b7e41

# インストール

```bash
$ $GOPATH/bin/gocomplete -install
Install completion for go? y
Installing...
Done!
```

## 2022/10/3更新

```bash
$ COMP_INSTALL=1 go/1.19.1/bin/gocomplete
install completion for go? y
installing...
```

上記のインストールコマンドの実行により、.bash_profileにgocompleteを有効化するための情報が書き込まれます。

```bash:.bash_profileに書き込まれる情報
complete -C /Users/user/go/bin/gocomplete go
```

## 2022/10/3更新

```bash
complete -C /Users/user/go/1.19.1/bin/gocomplete go
```

# 有効化
シェルを再起動するか、以下の様に.bash_profileを再読込すれば有効化されます。

```bash
$ source .bash_profile
```

# Reference
https://godoc.org/github.com/posener/complete

https://github.com/posener/complete/tree/master
再帰処理の流れについて基本的な理解を目指す

　「自分自身を呼び出す」再帰的関数に関するコード記述を読んでも、なかなか理解が追い付かずに折れかけたので、自戒を込めて「再帰処理の流れについて基本的な理解を目指す」をテーマに記事を書いてみたいと思います。
　下記のシンプルなコードを用いてどのような流れで関数が実行されているか、考えてみます。

####再帰的関数

```rb
1  def recursive(n)
2     p 'a'
3     p n
4     return 1 if n == 0
5     p n
6     n * recursive(n - 1)
7     p 'b'
8     p n
9  end
10 
11  p recursive(5)
```
実行すると、下記のとおり。
![recursive1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698990/93bfd2e6-6896-78de-c445-52691ed426c5.png)

初見、なぜ数字が0から増えていくのかがまったく分からずに混乱しました。
「関数が関数を内包している」様子をイメージしながら、再帰的関数の仕組みについて、下記のように自分なりにかみ砕いてアウトプットしてみます。

####おおまかな流れ
 (1)引数として5が与えられて関数実行
→(2)p "a"から5行目のp nまでが実行される（※当然、後置if文returnは実行されない）
→(3)6行目で、自身の関数recursive(4)が実行される。
→(4)ループのように、上記(2)から(3)が、recursive(0）まで実行される。（※この時点で、「（省略）..."a", 1 , 1」まで処理されている」）
→(5)recursive(0)実行時、 p "a"から3行目のp nまでが実行されたのち、後置if文 returnが実行される。returnにより、recursive(0)関数処理が終了。
→(6)ここからは、巻き戻し？逆行？の様な状態になる。
    実行場面がrecursive(1)に戻り、7行目の"b"、8行目のp n（※nは1）が実行され、recursive(1)終了。
→(7)実行場面がrecursive(2)へ戻り、7行目の"b"、8行目のp n（※nは2）が実行され、recursive(2)終了。
→（recursive(4)まで省略）
→(8)実行場面がrecursive(5)へ戻り、7行目の"b"、8行目のp n (※nは5）が実行され、recursive(5)が終了しすべての関数が完結。
→(9)最後に、p recursive(5)（p nと同義で）5が出力され、終了。（※仮にpメソッドを除くと、最後の5は出力されない。）

・・・個人的に再帰的関数はかなり理解が難しいです。
場数をこなして慣れていくのがいいのかなと少し頭を悩ませつつ、筆をおきます。:confused:
WordPressでaタグが勝手に閉じまくった原因

# WordPressでaタグが記述通りに動いてくれない
最近のaタグはブロック要素の中に書かなくてもいいという話を聞き、  
固定ページで↓みたいに書いてみたんだけれど、どうしてもうまく行かない。。

```
<a href="#">
    <div>
        <h3>hoge</h3>
        <p><a href="#">hogehogehoge</a></p>
    </div>
</a>
```

↓みたいになっちゃう

```
<a href="">
    <div></div>
</a>
<a href="">
    <h3>hoge</h3>
</a>
<a href="">
    <p><a href="#">hogehogehoge</a></p>
</a>
```

調べてみても解決方法は出ていなかったのですが、ある記事に

>HTMLはブロック要素の中にインライン要素を入れるというルールがあって〜

という記述を発見！

# 苦しい言い訳と今回の対応
そのルール自体は知ってたけれど、モダンなコーディングは気にしなくていいとか聞いたから
***「モダンなコーディングでナウく作ってやるゼ」***
とか考えて作ったのが裏目に出た様子。。

つまり
- div
- h3
- p

はブロック要素だから分解されちゃったわけだ(でもそれならそもそも囲われるのがおかしくないかと思うけど。。)

少しもやりはありつつも、今回はカード全体をクリッカブルにするのは諦めることに。

絶対なんか方法あると思うけど、優先度高くないからそんなに時間かけられないのだ。

ただ、このあたりについては対応できるようになってないとだと思うので、WP構築して勉強しよう。

# 端書き

ちなみにWordPressの自動挿入を無効化してくれる方法があるらしい。

https://wp-topics.com/howto/461/

今回は会社のWPだったので使えなかったけれど、自分で遊ぶときに使ってみようかと思う。
リングバッファのBit演算化(高速化)

#経緯
リングバッファを使用する際に余剰を使用してインデックスを計算していた。職場のプログラマの方に「ビット演算でもできるよ」と教えてもらったのでここにメモしておく。

#Code(Python)
```Python:ring_buffer.py
# coding:utf-8
'''
Ring Bufferがビット演算で早くなるか実験
'''
import time
import numpy as np

def main():
    ring_buff = np.array([i for i in range(1024)])

    #余剰演算ver
    start = time.time()

    for cnt in range(102400):
        index = cnt % 1024
        ring_buff[index] = cnt

    elapsed_time = time.time() - start
    print ("余剰ver   :{0}".format(elapsed_time) + "[sec]")    
    
    #bit演算ver
    start = time.time()

    for cnt in range(102400):
        index = cnt & 0b1000000000
        ring_buff[index] = cnt

    elapsed_time = time.time() - start
    print ("bit ver   :{0}".format(elapsed_time) + "[sec]")

if __name__ == '__main__':
    main()
【Clean Architecture】クリーンアーキテクチャが伝えるシンプルなルール


Robert C. Martinによる「Clean Architecture　達人に学ぶソフトウェアの構造と設計」を読んだまとめ。
クリーンアーキテクチャは例の同心円の図で説明され一見複雑に見えるが、シンプルなルールからなる。

## 優れたアーキテクチャとは？

これまでの優れたシステムのアーキテクチャでは、以下のような特性を持つ。

- フレームワークに依存しない
  アーキテクチャは、ソフトウェアのライブラリに依存しない。

- テスト可能
  ビジネスルールは、UI、データベース、ウェブサーバーなどの外部要素がなくてもテスト可能である。

- UI 非依存
  UI は、システムの他の部分を変更することなく、簡単に変更できる。

- データベース非依存
  DB の種類に依存せず、置き換え可能である。

- 外部エージェント非依存
  ビジネスルールは、外界のインターフェ―スに関して知識を持たず、依存しない。

クリーンアーキテクチャは、これらの特性を統合したアーキテクチャである。

## 概要

クリーンアーキテクチャでは、システムの概念に境界線を引く。
この境界線が、それぞれが分離していることをより明確にし、疎結合なシステムを実現する。

下位の概念は、`DB、UI、ウェブ`等である。
通常、このレイヤーにはあまりコードを書かないようにする。なぜなら、このレイヤーには詳細が詰まっているためである。
GUI における `MVC アーキテクチャ`等も下位の概念である。`外部サービスとのアダプター`なども、このレイヤーに位置する。

上位の概念は、ビジネスルールを表現する`エンティティ`、アプリケーション固有のビジネスルールを含む`ユースケース`、などである。
エンティティは、最も一般的で、最上位レベルのルールをカプセル化しており、外部で何か変化が起きても変化する可能性は低い。
ユースケースは、システムのユースケースがカプセル化され、実装されており、DB や、UI などの下位の変更を受けることがない。

## 依存性のルール

クリーンアーキテクチャを動作させる最も重要なルールは、依存性のルールである。

> ソースコードの依存性は、内側(上位レベルの方針)だけに向かっていなければならない。

この説明は、クリーンアーキテクチャの同心円を説明するものだが、**依存を 1 つの方向に限定し、常に上位の方針に向いている必要がある**という事である。
外側(下位の概念)で宣言された名前を、内側(上位の概念)のコードで触れない。上位の概念は、下位の概念に影響を受けたくない。

## 依存性を管理するために

システムを構築する上で、上記のルールとは明らかに対立する依存関係も出てくる。この場合、**依存関係逆転の原則(DIP)**を使って解消する。
上位概念から下位の概念の具象に触れたい場合、インターフェースを呼び出すようにして、制御の流れとは反対のソースコードの依存関係を生み出すことが出来る。

## 上位概念が下位からデータを受け取る場合は？

概念の境界を超えるデータは、オブジェクトでも、ハッシュマップでも良いが、独立した単純なデータ構造であることが必要である。
ただし、上位概念にとって便利な形式にする。**上位概念は、下位概念について深く知る必要がない**ためである。

## テスト可能

ユースケースが中心となった外部要因に依存しないシステムアーキテクチャでは、フレームワークを使うことなく全てのユースケースのユニットテストを実行できる。
テスト実行のためにウェブサーバーやデータベースを起動する必要がない。
エンティティももちろん、単体でテスト可能である。

## まとめ

クリーンアーキテクチャが一貫して実現しようとしているのは、**依存性の管理**と、それによるレイヤー間での**疎結合**の実現である。
自分自身最初に例の同心円を見た時、なかなか表現したい世界が理解できなかったが、シンプルなことを実現しようとしていることを理解できれば、より堅牢なシステムを構築できるようになるだろう。
最初の記事の投稿

#変数について

 変数の宣言（これから〇〇という変数を使いますよ）
 【書式】var 変数名 = 値
 「＝」記号は、右辺の値を　左辺の変数に代入すること

```swift

var moji = "Hello Swift!!"
var suji = 2020

print (moji)
print(suji)
```

変数は箱に例えられることが多いけど、
エコバックみたいなイメージかも。
いろんな形といろんな色の種類を持つことができる。

エコバックがvar、ばぁ。
#定数について

 定数：書き換えが出来ない変数のこと
 【書式】 let 定数名 = 値

```swift
let PI = 3.14159
let menseki = 5 * 5 * PI

print(menseki)
```

定数はコンビニのレジ袋。
好きなの選べない、形も色も変えられない。
(注)これから勉強していって違うなと思ったら書き換えます。

レジ袋がレット。

#変数の宣言（データ型指定）
 【書式】 var 変数名 : データ型 = 値
 整数型：Int/UInt/Int8
 少数型 : Double/Float
 Bool型 : true/false
 文字列 : String
 
 ▶︎型推論による変数宣言(データ型を調べたい場合）
 【書式】type(of : 変数名)

```swift

var x = 5
var y = 3.14
var z = "A"

print(type(of: x))
print(type(of: y))
print(type(of: z))
```

#オプショナル型の変数宣言
 「なにもない」状態ーnil(ニル)と呼ぶ。
 データ型の後ろに？を付けたオプショナル型を使用する
 
 【書式】var 変数名 : データ型? = 値
 
 ▶︎オプショナル型変数をprint命令で表示した場合、「optional(値)」となり、
 通常の変数や定数とは異なる。
 ▶︎オプショナル型の値は、
 そのままでは通常の定数や変数と組み合わせて使用することができない。
 ▶︎オプショナル型変数にnilが入っていないことがわかっている場合は、
 アンラップ(Unwrap)処理をして使用することができる。
 「！」をつける。
 　→つけなかったらエラーが出た

```swift

var sujisuji: Int? = nil
var mojimoji: String? = nil

sujisuji = 3
mojimoji = "データ"

print(sujisuji)
print(mojimoji)

//アンラップ処理
var num1 = 2
var num2: Int? = nil

num2 = 3
print(num1 + num2!)
```

#Qiita

##の 

###練習

超絶基礎でした。
本日は初記事、Qiitaの練習を実施しました。
Gitlab CIのベストプラクティス

## Gitlab CIの仕組み

- Gitlab CIとGitlab Runnerという登場人物がおり、前者はテストプロジェクトの管理を、後者はテストの実行を行う。
  - CI：Runner＝1：多
    - Runnerはテストの並列実行や分散実行が可能
  - RunnerはGoで書かれた1つのバイナリファイルのため、どんなOSのVMでも導入可能

## Best Practices

### ビルドディレクトリ

- ビルドディレクトリとはRunnerがリポジトリをcloneするベースパスである
    - デフォルト値は下記の通りRunnerの環境に依存（公式docではRunnerの環境のことをテスト実行者という意味でexecutorと呼んでいる）
        - [Kubernetes](https://docs.gitlab.com/runner/executors/kubernetes.html), [Docker](https://docs.gitlab.com/runner/executors/docker.html) and [Docker Machine](https://docs.gitlab.com/runner/executors/docker_machine.html) executors, it is `/builds` inside of the container.
        - [Shell](https://docs.gitlab.com/runner/executors/shell.html) executor, it is `$PWD/builds`.
        - [SSH](https://docs.gitlab.com/runner/executors/ssh.html), [VirtualBox](https://docs.gitlab.com/runner/executors/virtualbox.html) and [Parallels](https://docs.gitlab.com/runner/executors/parallels.html) executors, it is `~/builds` in the home directory of the user configured to handle the SSH connection to the target machine.
        - [Custom](https://docs.gitlab.com/runner/executors/custom.html) executors, no default is provided and it must be explicitly configured, otherwise, the job fails.
    - `builds_dir`という変数で指定可能
- テストプロジェクトの推奨格納先は`{builds_dir}/$RUNNER_TOKEN_KEY/$CONCURRENT_ID/$NAMESPACE/$PROJECT_NAME`
  - 例：`/builds/2mn-ncv-/0/user/playground`
- テスト実行中にビルドディレクトリを操作してはいけない
  - Runnerにガードされないが、テストの挙動が不安定になる
  - CIに必要なものをテスト中に用意したい場合は別のディレクトリに入れること

### Graceful Shutdown

- Runnerはテスト実行中、Artifactsのダウンロード/アップロードやキャッシュの処理を行うので、killする際にはGraceful Shutdownの考慮が必要
    - 上記処理は`gitlab-runner`コマンドで実行される
- Graceful Shutdownの方法は2つ
    1. Runnerをサービス登録する。その際、killシグナルを`SIGQUIT`に設定する。止める際は、`gitlab-runner stop`や`systemctl stop gitlab-runner.service`で止める
    2. `kill -SIGQUIT <pid>`で止める。pidはRunner実行時のログから拾う。

     ```shell
     $ gitlab-runner run
     Runtime platform                                    arch=amd64 os=linux pid=87858 revision=8d21977e version=12.10.0~beta.82.g8d21977e
     ```



## 参考

- [簡単にわかるGitLab CIの仕組み](https://muunyblue.github.io/015389e74278202135dd6ed947ed32da.html#:~:text=GitLab%20Runner%20is%20an%20application%20which%20processes%20builds.&text=GitLab%20Runner%E3%81%AF%E3%83%93%E3%83%AB%E3%83%89%E3%81%AE,CI%E3%81%A8%E9%80%A3%E6%90%BA%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82)
- [Gitlab Runner Best Practices](https://docs.gitlab.com/runner/best_practice/index.html)
- [Gitlab Runnerコマンド一覧](https://docs.gitlab.com/runner/commands/README.html)
【Rails6】単体テスト時、ActiveRecordの暗黙的型変換でハマった話


# 事象
以下の単体テストが失敗してしまう。

```ruby:item_spec.rb
    it 'priceが半角数字でなければ保存できないこと' do
      @item.price = '１０００'
      @item.valid?
      expect(@item.errors.full_messages).to include('Price Half-width characters.')
    end
```

# 結論
以下2点を実施することで解決
①price_before_type_castに対してバリデーションを設定
②price_before_type_castがpriceとして、エラーメッセージに表示されるように設定

price_before_type_castとは、暗黙的型変換が行われる前のpriceの値が格納された項目です。
ActiveRecordには「カラム名\_before_type_cast」という項目が標準で用意されております。
つまり現在、priceには数値「0」、
price_before_type_castには文字列「１０００」が格納されていることになります。

本題に戻ります
①price_before_type_castに対してバリデーションを設定

```ruby:item.rb
class Item < ApplicationRecord
  # 価格は半角数字のみ
  validates :price_before_type_cast,
  format: { with: /\A[0-9]+\z/, message: 'Half-width characters.' }
end
```
しかし、このままではエラーメッセージが「Price before type cast Half-width characters.」と表示されてしまい、単体テストはまたエラーとなります。Webページにも同じエラーメッセージが表示されてしまうため、困ります。
そこで②を実施します。

②price_before_type_castがpriceとして、エラーメッセージに表示されるように設定

```ruby:application.rb
module アプリ名
  class Application < Rails::Application
    # 以下2行を追記
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml').to_s]
    config.active_model.i18n_customize_full_message = true
  end
end

```


```ruby:locales/en.yml
en:
  activerecord:
    attributes:
      item:
        price_before_type_cast: Price
```
これでprice_before_type_castのバリデーションエラーはpriceとして表示されるようになります。

単体テストも無事に通りました🎉

#原因
integer型の項目「price」への文字列「１０００」代入時、
暗黙的型変換が行われ、priceには数値「0」が代入されたため。

↓暗黙的型変換が行われる様子をコンソールで確認↓

```
irb(main):002:0> @item.price = '１０００'
=> "１０００"
irb(main):003:0> @item.price
=> 0
```
文字列「１０００」を代入した後のpriceの値は数値「0」となります。

これはRails 5から追加された「ActiveRecord Attributes API」という機能によるものです。
暗黙的型変換は裏で以下のようなセッターが動いているイメージです。
to_iしているところがポイントです。


```ruby
  # セッター
  def price=(price)
    @price = price.to_i
  end
```




# 【参考リンク】
「ActiveRecord Attributes API」の仕組みについては、以下の記事を参考にさせていただきました。
https://www.wantedly.com/companies/wantedly/post_articles/31132
【Ruby on Rails】Rails APIモード キーワード複数検索機能を配列と繰り返し処理を使って実装してみた。

## 機能として成り立つ前
```ruby
def keyword
    if params[:keyword]
      keywords = params[:keyword].split(/[[:blank:]]+/).select(&:present?)
      keywords.each do |keyword|
        offices = Office.where('name LIKE ? OR address LIKE ? OR near_station LIKE ? OR introduction LIKE ? OR company LIKE ?',"%#{keyword}%", "%#           {keyword}%", "%#{keyword}%", "%#{keyword}%", "%#{keyword}%")
      end
    else
      pagy, offices = pagy(Office.all)
      pagy_headers_merge(pagy)
    end
    render json: offices, each_serializer: OfficeIndexSerializer, include: '**'
  end
```
このような記述で彷徨っていた。理由としては、１単語では検索可能だが、複数キーワード（例えば,「東京　福岡」のような検索方法では、福岡という文字だけでしか検索できないような状態だった。

## 気になっていた点

- each文を使って繰り返し処理を書いているが、これがそもそも機能していないっぽい。
- where句の中身の記述があっているのか自信がない・・・。

## 最終的なコード
```ruby
  def keyword
    if params[:city_id]
      offices = Office.where(city_id: params[:city_id])
    elsif params[:keyword]
      keywords = params[:keyword].split(/[[:blank:]]+/).select(&:present?)
      offices = []
      keywords.each do |keyword|
        offices += Office.where('name LIKE (?) OR address LIKE (?) OR near_station LIKE (?) OR introduction LIKE (?) OR company LIKE (?)',"%#{keyword}%", "%#{keyword}%", "%#{keyword}%", "%#{keyword}%", "%#{keyword}%")
      end
    else
      pagy, offices = pagy(Office.all)
      pagy_headers_merge(pagy)
    end
    render json: offices
  end
```
変わった点としては、``offices = []``を加えた。キーワードを格納する箱を作るイメージ。
そして、``office += Office.where``と、where句内の?に()を加えた。


これにより、「東京」というような１単語のみのキーワード検索のみならず、「東京　福岡　千葉」というような複数キーワードに関しても検索機能として成り立たせることができた。
プログラマとして何を勉強したらいいか分からなかった頃に書きためたメモ

# キーワード集
何から勉強していけばいいか分からなかった頃のメモをQiitaに移動

# これからも逐次増やしていく予定
SOLID原則
オブジェクト指向プログラミング
ダックタイピング
デザインパターン
DDD (ドメイン駆動設計)
BDD
データバインディング
DI (依存性注入)
RDD 責任駆動設計
NullObject Pattern
防御的プログラミング

テストファースト
テスト駆動開発
TDD
モック、スタブ、ドライバ

リファクタリング

メタプログラミング

GOOSスタイル
関数プログラミング

kubernetes
docker
SQLアンチパターン
冗長性

lint check
PEP8
コメントスタイル
GitFlow

リーン・スタートアップ
アジャイル
スクラム
XP

継続的デリバリー
DevOps
ML-Ops
Kubeflow

競プロ

### デメテルの法則
メッセージを送ることができるオブジェクトの集合を制限する

### 意識すべき
見通しが良い : Transparent
合理的 : reasonable
利用性が高い : usable
模範的 : exemplary

---

コンポジションはhas-a関係を持つ
包含される側のオブジェクトが包含する側のオブジェクトから独立して存在しない
例: 料理が食べられれば、前菜もなくなる

集約は包含されているオブジェクトの存在が独立している
例: 学部と教授

---

テストすべき
・受信メッセージは返す値を確認する
・送られたこと確認にする、送った内容は確認しない

---

Actionは大きく分けて2つ
クエリかコマンドのどちらか

---

・コメントをする必要がない
もし必要があるなら、メソッドの粒度を見直すべき

---

依存方向の選択 >> 自身より変更されないものに依存する
データではなく、振る舞いに依存する

バリューオブジェクトをストラテジーで入れて使用する

データにメッセージを受け付けるオブジェクトであるかのように扱うと、新たに２つの問題が生じる

---

## SREチーム
* 可用性の確保
* レイテンシの低減
* パフォーマンスの管理
* 作業の効率化
* 変更管理
* モニタリング
* 障害対応
* キャパシティ管理

といった日常業務を担当し、ソフトウェア開発の能力が求められる。

---

なんの本で読んだか忘れた言葉
「オブジェクトに基づく設計から、メッセージに基づく設計にシフトした時、...」忘れた。確かDDDかRubyで学ぶオブジェクト指向設計の本に書いていた気がする

---

なんかメモを整理するのが楽しくなってきた
JSFで文字列のリストをカンマ区切りの文字列で出力する方法

- 環境
    - CentOS Linux release 7.8.2003 (Core)
    - openjdk version "11.0.7" 2020-04-14 LTS
    - JSF 2.3.9

![スクリーンショット 2020-09-24 22.48.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/08f66c13-a894-b679-d827-870e591bf70a.png)

# 方法1 : Beabでカンマ区切りの文字列用にGettrを作る
参考 : [Javaでカンマ区切りで文字列連結するいくつかの方法 - Qiita](https://qiita.com/shisama/items/b27d16b3aeb1baf055b1)

```xml:xhtml
<!--省略-->
<h5>文字列のリストをカンマ区切りの文字列で出力する</h5>
<h:outputText value="#{sampleBean.commaDelimitedString}" />
<!--省略-->
```

```java:SampleBean.java
// 省略
/** 文字列のリスト. */
@Getter
private List<String> strings = Arrays.asList("文字列の", "リストを", "カンマ区切りで", "表示したい。");

/**
 * 文字列のリストをカンマ区切りの文字列として取得する.
 * @return カンマ区切りの文字列
 */
public String getCommaDelimitedString() {
    return this.strings.stream().collect(Collectors.joining(","));
}
// 省略
```

# 方法2 : xhtmlで`ui:repeat`する

```xml:xhtml
<!--省略-->
<ui:repeat var="string" value="#{sampleBean.strings}" varStatus="index">
  <h:outputText value="#{string}" />
  <ui:fragment rendered="#{!index.last}">,</ui:fragment>
</ui:repeat>
<!--省略-->
```

```java:SampleBean.java
// 省略
/** 文字列のリスト. */
@Getter
private List<String> strings = Arrays.asList("文字列の", "リストを", "カンマ区切りで", "表示したい。");
// 省略
```
画面でエフェクトの実現の(一)　--ダイアログのポップアップ

一般的に、画面でボタンをクリックすると、bootstrapスタイルのダイアログ（modal）を表示する。

#画面なかに実装方法
・ダイアログをポップアップするボタンを指定する
・ポップアップするダイアログをdivで編集して、スタイルを設定する
#HTMLソースで説明する
HTMLだけでダイアログを実現できる。:clap:
・ボタンで「data-toggle="modal"」「data-target="#modalId"」しかを指定しない、別のonclickなど指定する必要がない。
```<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#modalId">
	ボタン
</button>```

・具体的なダイアログを設定する。idは一致です。「class="modal fade"」のcssのスタイルでボタンクリック前に非表示で、クリックすると、表示された。
```<!-- モーダル・ダイアログ -->
<div class="modal fade" id="modalId" tabindex="-1">
	<div class="modal-dialog">
		<div class="modal-content">
			モーダルの内容
		</div>
	</div>
</div>```
もし、モーダルが表示されたら、イベントが必要でしたら、下記のjavascriptを作る。

```$(#modalId).on('shown.bs.modal', function (e) {
	// 処理
});```

いいリンク
http://bootstrap3.cyberlab.info/javascript/modals-events.html
https://bootstrap-guide.com/javascript/modals/events

doc:
https://getbootstrap.jp/docs/4.2/getting-started/introduction/#%E3%82%B3%E3%83%9F%E3%83%A5%E3%83%8B%E3%83%86%E3%82%A3
[PlayCanvas]他のEntityのScriptを取得する

# 概要
例えば、衝突処理などが発生した時に、そのぶつかった対象に影響を与えるなどをするために必要な処理。

# 実装例
``` javascript:Sample.js
var Sample = pc.createScript('sample');

Sample.prototype.test = function( param ){
    console.log( param );
};
```

``` javascript:Call.js
var Call = pc.createScript('call');

Call.prototype.initialize = function(){
    var entity = ~~~; // 何かしらの手段でEntityを取得したとする

    // 安全でないアクセス
    entity.script.sample.test('Unsafe Call.');

    // 安全なアクセス方法
    // 1. EntityにScriptコンポーネントがあるかを調べる
    // 2. Scriptコンポーネント内にScriptがあるか調べる
    if(entity.script && entity.script.has('sample')){
        entity.script.sample.test('Safe Call.');
    }
};
```

PlayCanvasはEntityにScriptというコンポーネントを追加して、そこにJavaScriptで書かれたScriptを追加していく方式なので注意が必要。
SOLID原則

# SOLID原則
https://postd.cc/solid-principles-every-developer-should-know/

S：SRP、単一責任の原則
O：OCP、解放閉鎖の原則
L：LSP、リスコフの置換原則
I：ISP、インタフェース分離の原則
D：DIP、依存性逆転の原則
GitHubでプルリクエストをマージしてブランチを消したあとの流れ

## 概要
- プルリクエストをマージしたあとに、GitHub側（remote側）のブランチを消したあとの流れがよく分からなかったので整理してみた。

## 前提
- 書いた人は普段業務でSubversionしか使ってない人です。
- 以下のブランチがあることを想定
    - mainブランチ
    - featureブランチ
- 今回、feature → mainにマージして、featureブランチを削除する

## 手順
1. プルリクエストがマージされたときにGitHub上のfeatureブランチを削除
2. ローカル側でmainブランチに移動 `git checkout main`
3. GitHub側のmainブランチにマージした内容を、ローカルのmainブランチに反映する `git pull origin main`
4. ローカル側のfeatureブランチを削除する `git branch -d feature`
二点を固定した円の最小二乗法による近似

直線と同じように、最小二乗法を用いて点P1,P2, ... ,Pnから円を近似することができます。一般的な手法は他の記事を参照してもらうことにして、ここでは始点P1と終点Pnを必ず通り、かつ誤差が小さくなるような円を求めます。また、そのような関数をpython(numpy)で実装してみます。

#方針

直線と同様に、各点に対して理論値と測定値の差（残差）を求め、それらの平方和を小さくするような値を求めます。

一般に円を表すためには中心座標$(x,y)$、半径$r$の3つのパラメータが必要ですが、2点$P_1,P_n$を通る円の中心は必ずこの2点の垂直二等分線上にあります。中心が決まれば半径も決まりますから、今回求めるパラメータは一つにできます。

# 方法
円の中心と半径をベクトルを用いてパラメータ表示し……などとやっても不可能ではないですが、計算が煩雑になってしまうので、別の方法を取ります。ひとまず$P_1(-a,0),P_n(a,0)$の場合を考えましょう。このとき、中心はy軸上の点$(0,y_0)$、半径$r^2 = y_0^2+a^2$となり、求める円の方程式は

```math
x^2+y^2-2yy_0-a^2=0
```

となります。この$y_0$を求めます。

各点に対する残差は、各点から中心までの距離と半径の差ですが、計算の簡単のためにあらかじめ自乗してから差を取ったものを考えます。つまり各点の残差は上の式に$(x_k,y_k)$を代入して

```math
x_k^2+y_k^2-2y_ky_0-a^2
```

です。これの平方和、すなわち

```math
\sum \{ x_k^2+y_k^2-2y_ky_0-a^2 \} ^2
```

を最小にするような$y_0$を求めます。この式は$y_0$の二次関数ですから、$y_0$の偏微分を0とおいて$y_0$を求めます。

```math
\begin{align}
\sum -4y_k( x_k^2+y_k^2-2y_ky_0-a^2 ) &= 0 \\
\sum x_k^2y_k+\sum y_k^3-2y_0\sum y_k^2-a^2\sum y_k  &= 0 \\
\end{align}
```

よって$y_0$は以下です。

```math
y_0 = \frac{\sum x_k^2y_k+\sum y_k^3-a^2\sum y_k}{2\sum y_k^2}
```

# 座標変換

さて、これを任意の点群に対して適用するには、点群に対して点$P_1(x_1,y_1)$が$(-a,0)$へ、点$P_n(x_n,y_n)$が$(a,0)$へ移るような変換をしないといけません。そのためには、

①$P_1$と$P_n$の中点$P_c$が原点に移るような平行移動を行う
②$P_1$と$P_n$がx軸上に移るような回転を行う

の2ステップが必要です。細かい計算は省きますが、次の変換を行えばいいことがわかります。

```math
\left( \begin{array}{c} x' \\ y' \end{array} \right) = 
 -\frac{1}{a}\left(
    \begin{array}{cc}
      x_1-x_c & -(y_1-y_c)\\
      y_1-y_c & x_1-x_c
    \end{array}
  \right)
\left( \begin{array}{c} x-x_c \\ y-y_c \end{array} \right)
```

ただし、$x_c,y_c$は$P_c$の座標、$a$は$|P_1P_c|$です。
この変換をすべての点に行ってから近似を行い、逆変換すれば中心・半径が求まります。

#実装

pythonで実装してみました。

```python
def approxByArc(points):
	start = points[0]
	end = points[-1]
	sample = points[1:-1]
	
	midpoint = (start + end) /2

	start2 = start - midpoint
	a = np.linalg.norm(start2)

	x = start2[0]
	y = start2[1]

	rotateMatrix = np.array([[x,y],[-y,x]]) * -1 / a

	def conversion(point):
		point = point - midpoint
		p = np.array([[point[0]], [point[1]]])
		p = rotateMatrix @ p
		p = np.array([p[0][0],p[1][0]])
		return p

	sample = np.apply_along_axis(conversion, 1, sample)

	xs = np.array([i[0] for i in sample])
	ys = np.array([i[1] for i in sample])

	p1 = np.sum(xs*xs*ys)
	p2 = np.sum(ys*ys*ys)
	p3 = np.sum(ys) * a * a
	p4 = np.sum(ys*ys) * 2

	y0 = (p1+p2-p3)/p4

	center = np.array([[0],[y0]])
	center = np.linalg.inv(rotateMatrix) @ center

	center = np.array([center[0][0], center[1][0]])
	center = center + midpoint

	radius = np.linalg.norm(center - start)
	return center,radius
```

ちゃんと動くか試してみましょう。

```python
points = []
points.append([60,50])
n = 40
for i in range(1,n):
	angle = math.pi / n * i
	radius = 10 + random.uniform(-0.4,0.4)

	x = 50 + radius * math.cos(angle)
	y = 50 + radius * math.sin(angle)

	points.append([x,y])
points.append([40,50])

r,c = approxByArc(np.array(points))
print(r)
print(c)
```

```python
[50.         49.99319584]
10.00000231483068
```

うまくいきました。

#おわりに

単純な座標に変換してから計算する方法はプログラムがわかりやすくなる反面、手計算には向きません。ですが、このような複雑なステップを手計算する人はいないので大丈夫です。たぶん。
pygribでgrbsファイルを開いてみた

grbsを使ってみた。
最初sqliteみたいな感じなのかなと思っていた。

```py
import pygrib
grbs = pygrib.open("dataset/gfs_4_20200921_0000_000.grb2")
g = grbs.select(name="V component of wind")[0]
g
```
```
9:V component of wind:m s**-1 (instant):regular_ll:unknown:level 0:fcst time 0 hrs:from 202009210000
```

g.latlons()で、grbsはクラスでfunctionを持っているんだと気づく。

```py
lats, lons = g.latlons()
lats, lons
```
```
(array([[ 90. ,  90. ,  90. , ...,  90. ,  90. ,  90. ],
        [ 89.5,  89.5,  89.5, ...,  89.5,  89.5,  89.5],
        [ 89. ,  89. ,  89. , ...,  89. ,  89. ,  89. ],
        ...,
        [-89. , -89. , -89. , ..., -89. , -89. , -89. ],
        [-89.5, -89.5, -89.5, ..., -89.5, -89.5, -89.5],
        [-90. , -90. , -90. , ..., -90. , -90. , -90. ]]),
 array([[  0. ,   0.5,   1. , ..., 358.5, 359. , 359.5],
        [  0. ,   0.5,   1. , ..., 358.5, 359. , 359.5],
        [  0. ,   0.5,   1. , ..., 358.5, 359. , 359.5],
        ...,
        [  0. ,   0.5,   1. , ..., 358.5, 359. , 359.5],
        [  0. ,   0.5,   1. , ..., 358.5, 359. , 359.5],
        [  0. ,   0.5,   1. , ..., 358.5, 359. , 359.5]]))
```

## Gribとは
そもそもGribって何ってなった。
GRIB は、グリッド化されたデータを配布するための世界気象機関の標準規格で、pygribがECMWFのGRIB API C ライブラリへのpythonインターフェースらしい。

githubで調べたら多分これかなというのがあった。
[latlons()のメソッドのURL](https://github.com/jswhit/pygrib/blob/e89d66dc70b696d7c2aa39fdd250141ed9aa40c9/ncepgrib2.py#L745)

インターフェースざっくり見てもどう使うのかピンとこなかった。
こっちのExampleのが分かりやすいかも
https://jswhit.github.io/pygrib/docs/

keyをたくさん持っているみたいなので、keyを調べてみた。
めっちゃ多いから結果は省略した

```py
g.keys()
```
```
['globalDomain',
 'GRIBEditionNumber',
 'tablesVersionLatest',
 'grib2divider',
 'is_efas',
 'angularPrecision',

 ...(省略)

 'md5Section7',
 'section8Length',
 'analDate',
 'validDate']
```

numpy arrayとして扱えるらしい

```py
  maxt = grb.values
  maxt.shape, maxt.min(), maxt.max()
```
```
(94, 192) 223.7 319.9
```

(94, 192)は、何キロかごとに区切ったのグリッドだと推測している。

### Levelを並べ替える
```
levels = np.array([g['level'] for g in grb])
indexes = np.argsort(levels)[::-1]
indexes
```
```
array([22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,
        5,  4,  3,  2,  1,  0])
```

### グリッドからLevelを加えた3次元のデータを作る
```py
x = grb[0].values.shape[0]
y = grb[1].values.shape[1]
cube = np.zeros([len(grb), x, y])
for i in range(len(grb)):
    cube[i,:,:] = grb[indexes[i]].values
cube_dict = {
    'data': cube,
    'units': grb[0]['units'],
    'levels': levels[indexes]
}    
cube.shape
```
```
(23, 361, 720)
```

とりあえず欲しい値は取れた。
BaseMapに描画してみたり、等圧線とか書きたい

この辺りを参考にしたら出来そう
https://github.com/scollis/HRRR/blob/master/notebooks/HRRR%20Grib.ipynb
Redmineの公式Docker Imageでメール送信エラー"dh key too small"

# Redmineの公式Dockerイメージでメール送信エラー"dh key too small"

## Redmine公式Dockerイメージを使っていてメール配信がなかなかうまくいかなかった
メール配信のテストを行うと以下のエラーが発生しました。

```
An error occurred while sending mail (SSL_connect returned=1 errno=0 state=error: dh key too small)
```

## 原因
どうやらメールサーバ側のDH鍵が短く、Redmine側のOpenSSLのセキュリティ条件を満たせていない様子。
Redmineのコンテナに入り、OpenSSLの設定(/etc/ssl/openssl.cnf)を調べたらSECLEVEL=2になっていました。
RedmineのコンテナはDebian系のようです。

```
CipherString = DEFAULT@SECLEVEL=2
```

## 対処
仕方がないのでSECLEVEL=1に下げたところメールが配信できるようになりました。
コンテナ内で下記のコマンドを打つことで2から1へ書き換えができます。

```
# sed -i -e 's/CipherString = DEFAULT@SECLEVEL=2/CipherString = DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf
```

コンテナの外からだったら下記のコマンド

```
# docker exec -i -t (Redmineのコンテナ) /bin/sh -c "sed -i -e 's/CipherString = DEFAULT@SECLEVEL=2/CipherString = DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf"
```

## おわりに
役に立ちましたらLGTMをお願いいたします。