OmniAICreator/Qiita-1.07M huggingface dataset:
ブラウザゲームを作ってみよう(その6:画像表示)

# はじめに
[その5](https://qiita.com/noji505/items/3c57e0b5fb945a5b3c2f)では図形表示を行いました。
今回は画像の表示を行いたいと思います。

# 画像表示
以下のサンプル画像を表示したいと思います。
[sample.png]
![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/740996/9a687bc3-447d-0c2f-c7f5-cab0d51ecaf2.png)

```html
  <img src="sample.png">
```
表示するだけなら上記のようなHTMLタグになります。
ただこれだと画像の内容が全て表示されてしまうため、画像ファイルは1枚のままで画像の一部分だけを切り出して表示させたいと思います。

画像ファイルの読み込みと表示は以下のようになります。


```javascript
var img = new Array( 1 );

var imgName = [ "sample.png" ];
loadImageFile( imgName );  // 画像ファイルロード

drawImageClip( img[0], 0, 0, 0, 0, 32, 32 );  // 画像表示

//-----------------------------------------------------------
// loadImageFile : イメージファイルロード
//          引数 : ファイル名
//        戻り値 : 無し
//-----------------------------------------------------------
function loadImageFile( fileName )
{
	for( var i=0; i<fileName.length; i++ ){
		img[i] = new Image();
		img[i].src = fileName[i];
	}
}

//-----------------------------------------------------------
// drawImageClip : イメージ描画
//          引数 : イメージ,表示X,表示Y,開始X,開始Y,幅,高さ
//        戻り値 : 無し
//-----------------------------------------------------------
function drawImageClip( objImg, x, y, sx, sy, w, h )
{
	if( objImg == null )	return;
	g.drawImage( objImg, sx, sy, w, h, x, y, w, h );
}
```

# 実行イメージ
上記を実行すると、以下のような表示になります
![sample02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/740996/bbeeb36b-2b80-6155-095e-70935ee2a5f6.png)

また、以下のように記述すると「下」の部分が表示されます。
```javascript
drawImageClip( img[0], 0, 0, 32, 0, 32, 32 );
```

![sample03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/740996/cb46253c-36ba-f439-ae42-99bf3ce5416b.png)

:::note info
drawImageClipメソッドの第4引数が「0」→「32」に変わっています。
この部分で画像表示の開始位置を変更しています。
:::

# サンプル
今回の内容を踏まえて以下のサンプルを作成しました。
カーソルキーを入力した方向の画像(↑なら上)が表示されます。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="zYPLebY" data-user="noji505" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/noji505/pen/zYPLebY">
  Qiita17_sample1(画像表示)</a> by nojima (<a href="https://codepen.io/noji505">@noji505</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

画像の切替は、以下のような配列を定義して行っています。

```javascript
  var drawCharas = [
    [0,0],  // 上
    [32,0], // 下
    [64,0], // 左
    [96,0]  // 右
  ];
```
このようにしておくと、後から画像のサイズが変わった時にも対応しやすいと思います。


# 最後に
画像を表示出来るようになるとゲームっぽさが出ますね。
次回は当たり判定を行いたいと思います。
ラムダ式について


# 初めに
ラムダとは何か　につまづいた時に自分なりに調べ、今後迷った時に見返すために記事を残します
他の方の記事では詳しく、わかりやすく載っていますが
自分の中で特に必要だと思う部分をまとめます。同じようにラムダ式で躓いている方の参考になるかは、わかりませんがご了承ください

# 早速

```
val aaaaa = {name: String-> "名前は${name}です"}
```
簡単に説明していきます
{}の中が無名関数と言われる**関数**で　{}で囲む書き方を**ラムダ式**と呼びます
ラムダ式の中でパラメータname の型を指定することで　""内のnameの型推論をしてくれます
ラムダ式では最後の行　上の文で言うところの""の中が自動的に戻り値になります
それに従い return は不要になるため　ラムダ式ではreturnは使えません


```
val aaaaa: (String)-> String = {"名前は${it}です"}
```
引数が一つの場合は it で引数を扱うことができます


# 参考にした記事
[[Kotlin] 第一級オブジェクトとは？ ラムダ式の書き方](https://pouhon.net/kotlin-rambda/1316/)
Oracle Autonomous Databaseのクローニングを試す

Autonomous Database には手軽にデータベースのコピーを行うクローニング機能があります。
ここではクローニングの操作を行い、どのようなコピーが行われるかを確認します。
　データベースのホームページの「More Actipons」メニューから「Create Clone」を選択することでクローニング画面に遷移します。クローニングには以下の設定を変更することができます。


|設定  |説明 |デフォルト  |
|:---|:---|:---|
|Clone type |クローニングのタイプ（Full Clone, Refreshable Clone, Metadata Clone） |Full Clone | 
|Clone source| クローン元の選択（Clone from database instance, Clone from a backup)|Clone from database instance|
|Preferred region|リージョンの選択|カレント・リージョン|
|Compartment|コンパートメントの選択|カレント|
|Display name|クローン先の表示名| Clone of {元のDB名}|
|Database name|新しいデータベース名|自動生成|
|Datbase version|バージョン|19c|
|OCPU count|OCPU数|1|
|Storage|データ量|1 TB|
|Password|ADMINユーザーのパスワード|無し|
|Access type|ネットワークのアクセス方法|Secure from everywhere|
|License type|ライセンスの選択|License Included|
|Contact Email|コンタクト先のメールアドレス|無し|

元データとしてテーブル、マテリアライズド・ビュー、シーケンスを作成し、統計情報を取得しました。

```SQL:
SQL> SELECT table_name, TO_CHAR(last_analyzed, 'YYYY/MM/DD HH24:MI:SS') FROM USER_TABLES WHERE table_name IN ('SALES', 'SALES_M');

TABLE_NAME                     TO_CHAR(LAST_ANALYZ
------------------------------ -------------------
SALES                          2022/03/20 09:04:02
SALES_M                        2022/03/20 09:04:10

SQL> CREATE SEQUENCE seq1;

順序が作成されました。

SQL> SELECT seq1.NEXTVAL FROM DUAL;

   NEXTVAL
----------
         1
…

SQL> SELECT seq1.NEXTVAL FROM DUAL;

   NEXTVAL
----------
        10
```

# Full Clone
　クローン方法に「Full clone」を選択するとデータベースの全データをコピーします。
![Full Clone](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181284/30668d3e-ee62-8f8c-683c-af723e2c77bf.png)

データは当然コピーされますが、統計情報は再取得されず、元の日付のまま移行されます。シーケンスはキャッシュがフラッシュされた状態で移行されるため、元のデータベースよりも大きい値で移行されます。

```SQL:
SQL> SELECT TABLE_NAME, NUM_ROWS, STATTYPE_LOCKED, TO_CHAR(last_analyzed, 'YYYY/MM/DD HH24:MI:SS') FROM USER_TAB_STATISTICS;

TABLE_NAME                       NUM_ROWS STATT TO_CHAR(LAST_ANALYZ
------------------------------ ---------- ----- -------------------
SALES                              918843       2022/03/20 09:04:02
SALES_M                            918843       2022/03/20 09:04:10

SQL> SELECT seq1.NEXTVAL FROM DUAL;

   NEXTVAL
----------
        21
```


# Metadata CLONE
定義情報のみをコピーする「Metadata Clone」を選択できます。Always Free 環境以外では「Refreshable Clone」も選択できます。Metadata Cloneを選択すると、テーブルやマテリアライズド・ビュー等の定義は移行されますが、データは移行されません。

![Metadata clone](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181284/c288da04-02b0-d594-a76b-d68ebc73a742.png)

データは移行されませんが、統計情報はそのまま残っています。また一般的に Data Pump で定義情報（METADATA）のみ移行すると統計情報がロックされます。しかし USER_TAB_STATISTICS　ビューの STATTYPE_LOCKED 列に値が入って
いないため、Metadata Cloneでは統計情報のロックは行われないことがわかります。

```SQL:
SQL> SELECT TABLE_NAME, NUM_ROWS, STATTYPE_LOCKED, TO_CHAR(last_analyzed, 'YYYY/MM/DD HH24:MI:SS') FROM USER_TAB_STATISTICS;

TABLE_NAME                       NUM_ROWS STATT TO_CHAR(LAST_ANALYZ
------------------------------ ---------- ----- -------------------
SALES                              918843       2022/03/20 08:58:52
SALES_M                            918843       2022/03/20 09:00:29
```


シーケンスはFull Cloneと同様にキャッシュをフラッシュした状態で移行されます。

```SQL:
SQL> SELECT seq1.NEXTVAL FROM DUAL;

   NEXTVAL
----------
        21
```
3Dの散布図をさくっと見るツールを作ってみた

# はじめに
3Dの点群の分布状況をさくっと見たいなと思って、ちょっとググってみたものの、そういうサービスが意外と見つからない。有料サービスだったり、ソフトだったり。２次元だったらExcelで見れるけど、３次元は見つけられない。

大したものは求めてないんだよ、JavaScriptでできるんじゃないのー？

というわけで、作ってみました。

# 作ったもの
CSVを入力すると、JavaScriptで絵を描いてくれる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/9b6b11c8-ff34-d091-9388-c52f9ede70e6.png)
デモサイト
https://yo16.github.io/scatter_3d_js/

ソース

https://github.com/yo16/scatter_3d_js


## 特徴
- ブラウザで動作します。ドラッグでぐりぐり動かせます。
- 自分で用意したCSVのデータも描けます。
- htmlと必要なJavaScriptをダウンロードすれば、ローカルでも動きます。
    - データの収集等はしていませんのでご安心を。
- JavaScriptがわかる人なら、自分のサイトに組み込めます。
- 別記事の「[グラフの軸は意外と難しい](https://qiita.com/yo16/items/ea620dc234286130e348)」で書いた、グラフの軸の範囲を適切（適当？）に決めるロジックが入ってます。




# 中身のこと
## 1) three.js
[three.js](https://threejs.org/)を初めて使ってみました。基本的には、素材情報（Material）と幾何情報（Geometry）を別に作って、合わせてMeshを作るという形。Materialは使いまわせるけど、Geoは１粒ずつインスタンス化するから、どうしてもメモリを使う印象。特に点群表示なんて、いかにもたくさんの要素が生まれそうなもの。どう作るのが最適なのかよくわからなかったです。

透過しないとかそういう細かい工夫を積み重ねるんだろうなとは思いますが、今回の目的は「さくっと点群が見たい」だし、初心者なのでスルーさせていただきました。（大量の点群を突っ込むと固まるよ、ごめんなさい、の言い訳）


## 2) requestAnimationFrame関数
JavaScriptの関数です。（[MDNドキュメント](https://developer.mozilla.org/ja/docs/Web/API/Window/requestAnimationFrame)）
描画関数の中で、このrequestAnimationFrame関数を非同期で呼び出して、もう一度描画関数を実行する、という再帰っぽい方法で描画をぐるぐる回すのが今の描画方法だそうです。知らなかった。

でも今回の私のツールのようなものは、大体の時間は止まっててCPU負荷がもったないないので、ドラッグしていない間はぐるぐるしない方法をちょっとだけトライしてみました。でもうまくいかず断念。いい方法があれば、改善のネタです。

止めたかった理由はCPU負荷もあるけど、もう１つ別のCanvasにグラフを描くと、あっちでもこっちでもぐるぐるするので、うまく動かなかったんです。（変数のスコープのせいかもしれないけど。。）というわけで、ちょっとそこは課題を感じてます。

## 3) 軸・・・
しつこいですが、軸を自動で決めるのが難しいです。まだバグがある。（軸の数値がおかしくなるのをたまに目撃する。）
[グラフの軸は意外と難しい](https://qiita.com/yo16/items/ea620dc234286130e348)

# おわりに
ちょっと見たい、というくらいであれば、十分耐えられるものを作れたのではないでしょうか。ちょっと見たいときに使ってみてください。
ちなみに私は、いい感じの分布のカラーパレットを作りたくてRGBを３次元と見立てた分布を見たかったです。適度に離れてるかなーとか、使ってない色はないかなーとかそういう風に。


そういう意味では十分使えるものができたし、とても勉強になって面白かったです。three.jsは、[d3.js](https://d3js.org/)同様に、何かの情報をインタラクティブに見せたいときに使う道具の１つになりうるなと思いました。
空間図形が好きな人とか、３次元データ系のお仕事の人は、これ作ってみるといいかもしれない。
屋外にデバイスを設置するのによさげなケース JB-WSB049070

屋外にデバイスを設置したい時があります。
なかなか適したケースが無く困っていましたが、オーム電機のJB-WSB049070Eがお勧めなので紹介します。
## JB-WSB049070Eとは
 __JB-W　キャプコン一体仕様　スリムブランクタイプ__ というカテゴリーの商品になります。
https://www.ohm.jp/parts/box/jb-wsb/
元々は中に何か入っていたようですがJB-WSB049070Eは基板を止めるネジ穴があります。
つまり、このまま基板などを入れて固定できるのです。
今回はJB-WSB049070EとJB-WSB034070Eを入手できたので比較してみます。
![DSC_0147 (2).JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/139b4447-4880-9e2a-6dc0-6ab122c2f7e5.jpeg)
## 入れるのに適したデバイス
私は元々M5Stampを前提に考えていました。
M5Stamp、M5StickC、RaspberyPi Picoなどを入れて確認しました。
![DSC_0151 (2).JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/4db92f4d-b308-515a-5750-5ad9070572ab.jpeg)
![DSC_0154 (2).JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/5cb1d754-d0c6-e3f6-c29d-e0817d1bf791.jpeg)
M5StickCを入れることは出来ましたがUSBケーブルは通線出来るとはいえ、端子が邪魔で中に入れるのはキツイことがわかりました。
![DSC_0155 (2).JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/69ae6e0a-4a15-41e3-5d3b-29be3f93f6db.jpeg)
この為、M5Stamp以外は適さないといえるでしょう。
（M5Atomなら入るのでは？）
また無理にUSBケーブルを入れるのもあまりお勧めできない為、M5Stampがベストといえそうです。
M5Stampはなかなか使う場所が限られていましたがこのケースには最適だといえます。
## 防水性能
このケースはIP67の耐候性があります。
IP67は完全防水ではありませんので完全な防水防湿を希望される環境には向きません。
しかし屋外でデバイスを設置したいというのであれば使えると思われます。
## 電源
ESP32はご存じの通り3.3Vで８００ｍA程度の電流を必要とします。
この為、屋外で使用する際に3.3Vを直接入力するのは電圧降下等からお勧めしません。
M5Stampが最適なのは5V入力に対応したレギュレータがある事です。
好ましい使い方とは到底言えませんが、5Vの電圧を印加して、M5Stampで降圧することが出来る事で
電圧降下や大きなDC/DCコンバータを用意する必要が無い事がメリットといえます。
もちろんJB-WSB049070Eはサイズ的に余裕がある為ここにレギュレータを搭載しても問題はありません。
## 総評
予想通りM5Stampを使用するのに最適な屋外用ケースだといえそうです。
【MetalKit】Drawable.texture assertion error

# MTKViewのtextureに画像が描画されない
以下が問題のコードです。

```swift
import UIKit
import MetalKit

class ViewController: UIViewController {
        
    @IBOutlet weak var metalView: MTKView!
    private let device = MTLCreateSystemDefaultDevice()!
    private var commandQueue: MTLCommandQueue!
    private var texture: MTLTexture!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setUpMetalView()
    }
    
    private  func setUpMetalView() {
        commandQueue = device.makeCommandQueue()
        metalView.device = device
        metalView.delegate = self
        texture = try! MTKTextureLoader(device: device).newTexture(
            name: "Image",
            scaleFactor: view.contentScaleFactor,
            bundle: nil)
        metalView.colorPixelFormat = texture.pixelFormat
    }


}

extension ViewController: MTKViewDelegate {
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    }
    
    func draw(in view: MTKView) {
        guard let currentDrawable = view.currentDrawable else {return}
        let commandBuffer = commandQueue.makeCommandBuffer()!
        let blitEncoder = commandBuffer.makeBlitCommandEncoder()!
        blitEncoder.copy(from: texture,
                         sourceSlice: 0,
                         sourceLevel: 0,
                         sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
                         sourceSize: MTLSizeMake(w = min(texture.width, currentDrawable.texture.width), min(texture.height, currentDrawable.texture.height), texture.depth),
                         to: currentDrawable.texture,
                         destinationSlice: 0,
                         destinationLevel: 0,
                         destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
        blitEncoder.endEncoding()
        commandBuffer.present(currentDrawable)
        commandBuffer.commit()
    }
}
```

# 解決策
以下のコードの通りframebufferOnlyの設定をfalseにしてあげる必要があるみたいです。
MTKViewのドローアブルのテクスチャに書き込みたい場合、フレームバッファのみでは描画できないらしく、レイヤーを構成する必要があるみたいです。そのためframebufferOnlyをfalseに指定し、レイヤーの構成が必要であることを伝える必要があるみたいです。

```swift
private  func setUpMetalView() {
    // 以上省略
    metalView.framebufferOnly = false
}
```

# 参考
https://stackoverflow.com/questions/39206935/metalkit-drawable-texture-assertion-error
Automatorを使ってCharlesを無限に起動させたい

# いや課金した方がいい
ぐうの音も出ない、、、
ただ、なんとなく制約があると突破したくなる、、、
と言うことで、MacのAutomatorを使ってCharlesを30分に1回再起動させたいと思います。
（Pythonでそれっぽいの作ったけど、Automaterでいい感じにできるらしいので作ってみる）

# 設計
下記の手順で無限起動させていきたい。
1. Charles起動
2. 29分後に通信を保存
3. Charlesを再起動
3. 2と3をループ

# 実装(?)

1. アプリケーションを選択。
![スクリーンショット 2022-02-23 21.07.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230993/66bfe4e4-502d-42e0-7ff2-811497df82fc.png)

1. 「ユーティリティ→AppleScriptを実行」を追加する。
![スクリーンショット 2022-03-20 20.44.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230993/c48361af-5b76-5c0a-8a21-81bac57d40b5.png)

1. 下記のコードを記述し、ビルド。
~~~applescript
repeat 5 times
	tell application "Charles"
		run
		delay 1740
		quit
	end tell
end repeat
~~~
![スクリーンショット 2022-03-20 20.45.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230993/ee50fdad-8351-344b-3573-5376f372f84c.png)

AppleScript、for文ぽいのも使えるのか〜！
こんなに簡単に書けるとは思ってなかった。

4.アプリケーションで書き出しして完成！！


# 感想
AppleScript、初めて使った。色々できそう。
本当はオートセーブさせたかったけど、難しかったので断念。
今度やれたらやろうと思います。

# 参考にしたページ
https://dev.classmethod.jp/articles/applescript-sample/
https://eastmanreference.com/complete-list-of-applescript-key-codes
https://koze.hatenablog.jp/entry/2015/05/26/000000
http://www.script-factory.net/monologue/FastDataOperation/index.xhtml
PHP8.0にアップグレードしたらメールヘッダがおかしくなった話

タイトルの通り，PHP7.4からPHP8.0にアップグレードするとmail()で送られるメールの本文にメールヘッダの一部が表示されてしまいます。

```php:mail
From - Sun Mar xx xx:xx:xx 2022
Delivered-To: hoge@foo.jp
Received: ............................
Date: .........................
To: hoge@foo.jp
Subject: the subject

From: bar@foo.jp

Mime-Version: 1.0

hello
```
1個目の空行から下がメール本文と認識されるためメール本文はこうなります。

```php:メール本文
From: bar@foo.jp

Mime-Version: 1.0

hello
```

原因がわからず，とりあえずPHPMailerを入れてSMTP経由で対応しました。

https://github.com/PHPMailer/PHPMailer/releases

PHPMailerの使い方はこちらを参考に。

https://www.webdesignleaves.com/pr/php/php_phpmailer.php


(追記:2022/3/24 原因が判明しました)

https://github.com/php/php-src/blob/master/ext/standard/mail.c

```php:php-src/ext/standard/mail.c(513~518行目)
		fprintf(sendmail, "To: %s\r\n", to);
		fprintf(sendmail, "Subject: %s\r\n", subject);
		if (hdr != NULL) {
			fprintf(sendmail, "%s\r\n", hdr);
		}
		fprintf(sendmail, "\r\n%s\r\n", message);
```
PHP7.4まではLFで出力していたのが，CR+LFに変更になった様子。

```php:php.ini
sendmail_path = /bin/cat > mail
```
で吐き出してエディタで見ると，確かに全てCR+LFになってる。
```sh
/usr/sbin/sendmail -t < mail
```
とするとやっぱり受信メールのヘッダに空行が入ってしまう。

sakuraエディタで受信メール(.emlファイル)をよく見てみると
![Art000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651534/07c0916b-ec62-581a-dbd0-585abe4587a6.png)
CR+CR+LFになってるな…。
ということで，どうやらPHP8.0で吐いたCR+LFをsendmailがCR+CR+LFに変換している模様。

とはいえ，PHP8.0でCR+LFで出力するのはどうしようもないので，sendmailに投げる前にnkfを挟んでPHP8.0が付けた改行コードCR+LFを一旦LFに戻してから，sendmailに送ってそこでもう一度CR+LFに変換するという力技...
```php:php.ini
sendmail_path = "nkf -Lu | /usr/sbin/sendmail -t -i"
```
として解決しました。

これはsendmailが悪いのか?
AndroidスマホでのUI変更 G-Visual-Mod

# なぜこのページを作ったか
GVMをどこからダウンロードすればいいのかわかりずらかったから。
# できること
![画像](https://forum.xda-developers.com/proxy.php?image=https%3A%2F%2Fi.imgur.com%2FB5XZBfZ.png&hash=9e6ccda5dd6f1d6c9e71681ffe7d01e1 "こんなことができます")
詳細はXDAを[こちら](https://forum.xda-developers.com/t/module-g-visual-mod-systemlessy-customize-your-androids-visual.4225571/)からどうぞ
私のMi11 Lite 5GのカスタムRom Pixel ExperienceではUI Radiusがコントロールセンターで正常に機能しませんでした

# 動作要件
Magisk 20+
Android Pie (9) and higher
Android Q (10) & Android R (11) for Pill Gesture Mod

# どこからダウンロードすればいいか
telegramからダウンロードするのが最もいいと思います。それ以外はインストール出来なかったり、バージョンが古かったりしている。
リンクは[ここ](https://t.me/geefeeds)から。
しかしtelegramをサインアップしたりインストールしたりしなくてはないのが不便。
Docker for windowsを使わずにWSL2でDockerを使用する

# はじめに
wsl2，ubuntu20.04にdockerをいれる
docker for windowsは使わない
参考サイト
- https://matsuand.github.io/docs.docker.jp.onthefly/engine/install/ubuntu/
# 前提条件
- windows 11
- wsl2 Ubuntu-20.04

# 準備
リポジトリのセットアップ
### aptのパッケージの更新・必要パッケージのインストール
``` Bash
sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
```
### Docker の公式 GPG 鍵を追加
``` bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```
### リポジトリの登録
``` bash
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
## Docker Engine のインストール
``` bash
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
```

## Docker Composeのインストール
``` bash
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

## 確認
``` bash
sudo docker run hello-world
```
問題なければ，下記のような結果が出力される
``` Bash
tlasu@XXXX:~$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:6d60b42fdd5a0aa8a718b5f2eab139868bb4fa9a03c9fe1a59ed4946317c4318
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```


# 追記
上までだと，毎回sudoが必要なので，下記を行うことがおすすめ。

https://qiita.com/tifa2chan/items/9dc28a56efcfb50c7fbe
Power Automate for desktop にチャレンジしてみた

# 同じフローをPower Automate for desktop で作ってみた

以前作ってみた「**[WinActorでテンプレートを作る](https://qiita.com/inaho3517/items/794e2a21917daff21f4f)**」を基に、Power Automate for desktop でRPAを作ってみることにした。

## Inputするデータ
こちらのCSVデータを使用する。
メモ帳で手入力で作成して、保存時にエンコードを「ANSI」に設定して保存している。
![20220326_CSV.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646663/84344b31-70d9-1c8f-2c53-5b62f3a71eda.png)

## Outputするデータ
そのままコピーをしてしまうと、**プロジェクトNoが日付として認識**されてしまうので注意。
また、消費税区分を基に売上金額を**税込金額として再計算して転記**する処理を加えている。
![20220326_XLSX.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646663/7d3665a7-bf89-ea7b-78ba-916d12a3c138.png)

## 実現するためのフロー
リスト型のフローなので処理内容を整理しながら作った。
**CSVの読み取り方**がWinActorとは異なっており、**プロパティの設定も自由度が高かった**ので、処理内容を試行錯誤して時間がかかったが、なんとか同じ処理ができた。
![20220326_PADフロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646663/31f3bde2-d7d5-5a8b-e3cd-300ed5989559.png)
変数の設定については、型は自動で設定してくれるので楽だが、日本語が使用できないことが少しツライ。
保守性を高めるためにも、関係者が把握しやすい命名規則を決めておく必要があると思われる。
![20220326_フロー変数.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646663/52df4f54-aad4-3737-19ae-1505928d3213.png)
今回はCSVデータをデータテーブルで取得した。
項目名の取り方は今回はこのような形で取得しているが、項目名がない形でも取得は可能。
一括でデータをとれるのは気持ちいいなと思った。
![20220326_CSVTable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646663/fdabcb8d-966d-8729-f14a-47de2562492e.png)

## 外部変数をつくってみた
ファイル名の変更があったりExcelの関数を変更したい場合に、Power Automate for desktop のフローにあるアクションのプロパティをいじらなくてもいいように、Excelにて外部変数を作った。
![20220326_外部変数.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646663/8a0d8513-d901-2e0f-34ed-b273dfe25079.png)
楽天モバイルで契約者変更してみた。(経験談)

## 前書き
楽天モバイルで契約者名義を変更しました。
***なぜか店舗では変更できず、郵送でのみ可能です。***

## 契約者名義変更手順
「楽天モバイル　契約者変更」とかでググると[楽天のページ](https://network.mobile.rakuten.co.jp/faq/detail/00001218/)が出てくる。
このページに沿ってやってく。

### 書類の準備
[必要書類のページ](https://network.mobile.rakuten.co.jp/guide/contract-succession/#step)から、 契約者情報登録・変更申請書をダウンロードしてきて1枚目（契約者変更 承認請求書）と2枚目(委任状)を記入する。1枚目は番号受任者が、2枚目は番号譲渡者が記載する。（それぞれ本人が記載すること！印鑑もあるので注意。）

また、受任者・譲渡者それぞれの身分証明書を準備。運転免許証の裏表をコピーすればOK。

必要な書類のまとめ
・1枚目
・2枚目
・身分証明書

### 楽天モバイルに送る
[必要書類のページ](https://network.mobile.rakuten.co.jp/guide/contract-succession/#step)にある送付先に送る。書類が多いので、一応94円分の切手にした。

## その後
楽天モバイルの対応待ち、、、
Strapi をできるだけ低コストで Fargate 化したけどうまくいかなかった

# 背景

Headless CMS を自前で運用してみたかったので、Docker Image が提供されている Strapi を動かしてみようと試みたが、うまくいかなかった記録。

https://strapi.io/

ローカルで動かすのはストレージ故障などの不安があり、一方で AWS 上で動かすと常時稼働分リソースの料金が気になる。どうしたものかと考えたところ、以下の構成に落ち着いた。

# 構成

- Fargate
  - 使うときに ecs:RunTask して FARGATE_SPOT の Strapi を起動
  - 使い終わったら ecs:StopTask
- Aurora Serverless
  - プライベートな利用なのでスリープからの復帰時間が生じても問題ない

## ポイント

Strapi の Docker Image は、初期起動時に yarn build などのセットアップを行う。環境によっては十分以上かかることもある。
Fargate 起動のたびにこれが生じるとさすがに待ち時間が辛いため、ローカルでセットアップさせ、完了後のコンテナを `docker commit` で改めてイメージ化し、Fargate で稼働させる。

# リポジトリ

https://github.com/tsubasaogawa/strapi-fargate-with-aurora-serverless

Terraform で以下を構築している。

- VPC
- Subnet
- IGW
- Route Table
- Security Group
- Aurora Serverless Cluster
- ECR
- ECS Cluster
- ECS Task Definition
- CloudWatch Logs

# 構築・起動

```bash
# インフラ構築
cd terraform
terraform apply -var 'master_password=XXX'

# ローカルでセットアップ開始
cd ..
docker compose up -d

# イメージ作成
make commit
# -> (1)

# イメージをプッシュ
make login
make push

# Fargate を RunTask
cd ..
make run_task
# -> (2)
```

# 生じた問題

## (1) docker commit しても一部ファイルがイメージに含まれない

Strapi がセットアップ時に生成したファイルが、commit したイメージに含まれていなかった。
原因は以下。

> コンテナ内でマウントされているボリュームに含まれるデータは、コミット作業に含まれません。
> http://docs.docker.jp/v19.03/engine/reference/commandline/commit.html

WORKDIR でもある `/srv/app` が、 Dockerfile で `VOLUME` 指定されている。

https://github.com/strapi/strapi-docker/blob/263a0e4085c4a0615cdfba2b7728ca1e30c25753/strapi/Dockerfile#L11

公式の Dockerfile をコピーして、VOLUME しないように書き換えることで回避は可能そうだが、少し気持ち悪い。
本問題を解消しなくても Fargate 上で再度セットアップされるだけなので、一旦はそのままにした。

## (2) KnexTimeoutError

Fargate を RunTask すると `KnexTimeoutError` が発生し、Strapi の起動に失敗した。

https://forum.strapi.io/t/heroku-error-knextimeouterror-knex-timeout-acquiring-a-connection-the-pool-is-probably-full-are-you-missing-a-transacting-trx-call/2618/8

調べてみると上記がヒットした。データベースのスペックが低いことに依るものとの記載があり、Aurora Serverless ゆえの問題かもしれない。
また、上記記事では yml を書き換えることでも解消したとの記載があった。やはり自前で Dockerfile を用意する必要があり、ここで力尽きた。

# まとめ

- せこいことをやろうとするとうまくいかない、という好例であった。
- 素直に API Gateway + Fargate + Aurora (not Serverless) という構成にすれば、おそらくすんなりと動いてくれるはずである。
Kotlin KoansでKotlin入門　第１５回：Ranges

# はじめに
公式の問題集[「Kotlin Koans」](https://play.kotlinlang.org/koans/overview)を解きながらKotlinを学習します。

過去記事はこちら
- Introduction
  - [第０回：Kotlin Koansについて](https://qiita.com/kosuke1/items/41d192af63485bfc8cb8)
  - [第１回：Simple Functions](https://qiita.com/kosuke1/items/5862a3ece96082a62e67)
  - [第２回：Named arguments](https://qiita.com/kosuke1/items/2af11262852ee41b5da7)
  - [第３回：Default arguments](https://qiita.com/kosuke1/items/16cb8ac780df58dc8054)
  - [第４回：Triple-quoted strings](https://qiita.com/kosuke1/items/52cfeed2c49b0252d40e)
  - [第５回：String templates](https://qiita.com/kosuke1/items/0221bbbc5bc70847be6f)
  - [第６回：Nullable types](https://qiita.com/kosuke1/items/aa96e422c5313b6961bc)
  - [第７回：Nothing type](https://qiita.com/kosuke1/items/1ca0e48820d58c9dfe16)
  - [第８回：Lambdas](https://qiita.com/kosuke1/items/8de74f9491e003127f53)
- Classes
  - [第９回：Data classes](https://qiita.com/kosuke1/items/1be2de1b4406b8d914bf) 
  - [第１０回：Smart casts](https://qiita.com/kosuke1/items/50bdecaa0740eb0e1a17)
  - [第１１回：Sealed classes](https://qiita.com/kosuke1/items/71a4da02fe1e99da5d63)
  - [第１２回：Rename on import](https://qiita.com/kosuke1/items/f11465cc15a94e7589a3)
  - [第１３回：Extension functions](https://qiita.com/kosuke1/items/d4a1da00bc142c297221)
- Conventions
  - [第１４回：Comparison](https://qiita.com/kosuke1/items/e9486d044cae32167f36) 


# 問題
[Ranges](https://play.kotlinlang.org/koans/Conventions/Ranges/Task.kt)

[ranges](https://kotlinlang.org/docs/ranges.html)を使用して、日付が最初の日付と最後の日付の間（を含む）の範囲にあるかどうかをチェックする関数を実装します。
任意の比較可能な要素から範囲を構築することができます。Kotlinでは、[checks](https://kotlinlang.org/docs/operator-overloading.html#in-operator)は対応するcontains呼び出しと...rangeTo呼び出しに変換されます。

```Kotlin
val list = listOf("a", "b")
"a" in list  // list.contains("a")
"a" !in list // !list.contains("a")

date1..date2 // date1.rangeTo(date2)
```

修正前のコード
```Kotlin
fun checkInRange(date: MyDate, first: MyDate, last: MyDate): Boolean {
    return TODO()
}
```


# 問題のポイント
range式は，in と !in で補われる演算子形式 ... を持つ rangeTo 関数で表現されます。

```Kotlin
if (i in 1..10) { // 1 <= i && i <= 10 に相当
  println(i)
}
```

# 解答例

```Kotlin
fun checkInRange(date: MyDate, first: MyDate, last: MyDate): Boolean {
    return date in first..last
}
```
Apacheアクセスログからユーザーエージェントを取り出す

Apacheのアクセスログからユーザーエージェントを抽出してTLS1.2以上に対応していないOS、ブラウザからどれくらいアクセスがあるかを調査する機会があったのでそのアウトプットです。

## ユーザーエージェント？TLS?
最初聞いたときは私はどちらも理解していなかったのでまずは用語を調べるところから始めました。

### ユーザーエージェント
インターネット利用者が利用しているOS、ブラウザの情報のことです。実際は下記の様な文字列が記載されています。（下記引用）
>たとえば、「Windows 10ユーザーがChrome バージョン90を使ってアクセスしてきた場合」は、以下のような文字列が、ユーザーエージェントとして識別される。
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36

https://webtan.impress.co.jp/g/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88


### TLS

TLS（Transport Layer Security）とは、安全に通信をするためのセキュリティプロトコルのことで、第三者が通信を覗けたり、改ざんできない様にするための仕組みです。同じセキュリティプロトコルのSSLの後継です。

https://ssl.sakura.ad.jp/column/ssl_tls/


今回行った作業はApacheのアクセスログからユーザーエージェント情報を抜き出して、利用者の使用しているOS、ブラウザがTLS1.2以上に対応したブラウザであるかの確認を行いました。現在はTLSv1.0,1.1は非推奨なので無効化したときのクライアントへの影響を調査するためです。


## アクセスログの確認
私が作業した環境は設定ファイルで別の場所に出力していましたが、ログの出力先はデフォルトで`/var/log/httpd/`配置されている様です。

少数のアクセスログから抽出するだけなら目視で確認も良いかもしれませんが、今回は一年分の膨大な情報を確認することがあったのでユーザーエージェントのみを抽出して種類ごとにカウントする集計コマンドを使用しました。

## awkコマンド
入力として受け取った文字列に対して、フィールド区切り文字やレコード区切り文字を指定し、「列」に対する処理を行うためのコマンドです。

https://qiita.com/bezeklik/items/f5c292c4360cde140bef

実際に使用したコマンドは以下になります。

:::note warn
コマンド実行により負荷をかけると問題があるので本番環境で直接行わない方がよいです。
:::

```
cat access_log* | awk -F '"' '{print $6}' | sort | uniq -c | sort -n -r
```

ログの設定にも依ると思いますが、私の作業した環境では１列目にユーザーエージェント種類ごとの数、２列目にユーザーエージェントの内容が出力されました。

このユーザーエージェントの内容をもとにTLS1.2以上に対応していない物はどれくらいあるのかをチェックしました。

参考にした情報

https://qiita.com/nightyknite/items/b2590a69f2e0135756dc#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E5%88%A5%E3%81%AEuseragent

https://www.nexa.co.jp/job/news/pdf/os_browser_list.pdf

数が多いのと、知らないことばかりで大変でしたが多くの学びになりました。
OpenCVで画像の明るさを取得する方法

# はじめに
画像の明るさを知りたいなと思い、OpenCVを使えば取得できるような話を聞いたので試しに調べでやってみました。
OpenCVを使って画像からHSV(色相/彩度/明るさ)情報を取り出せるので、そこから画像のおおよその明るさを調べることができそうです。

# やったこと
ファイルをを読み込んで明るさのヒストグラムをファイル出力します。画面には平均値と標準偏差を出力させます。

``` python3
import cv2
import numpy as np
from matplotlib import pyplot as plt

file = 'test.jpg'

#画像を読み込んでHSV形式に変換
img = cv2.imread(str(file))
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)

#ヒストグラムをファイル出力
plt.hist(v.ravel(),256,[0,256]);
plt.savefig("Brightness_hist.png")

#明るさの平均値と標準偏差を画面表示
print("mean: " + str(np.mean(v)))
print("std : " + str(np.std(v)))
```

# 実行結果１
フィンランドの夜の画像です。マクドナルドの看板がまぶしいですが夜なので暗いです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/8445ad2d-79a9-a09b-5b6b-41452f20bb59.png" width=50%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/3d1f681c-655d-1a48-12a4-fe50ffd2b105.png" width=50%>

```
mean: 70.97144158681233
std : 64.48446228449781
```
値が少ない箇所が突出していて、平均値が70くらいです。なんとなく暗めの画像だというのがわかります。

# 実行結果２
フィンランドの昼の画像です。雪景色で日中なので明るくなりそうです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/8ea2998f-a6e2-4eb0-ac8f-f16ce893912c.png" width=50%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1359985/b90e53f6-67d8-aa99-7d5a-2f5030d1af78.png" width=50%>
```
mean: 154.91464773813883
std : 57.38790912426793
```

値が大きい箇所に集まっていて、平均値が155くらいです。なんとなく明るめ画像だという事がわかります。
Blender 4.2で日本地図（四色問題）

## これなに

Blenderで日本地図の県ごとにオブジェクトを作り、4色に塗ってみます。


## 完成物

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13955/6550812e-86c2-98a0-1103-29a4bfcfab0d.jpeg" width="600">

- [完成物をSketchfabで見る](https://skfb.ly/otDJR)

## 四色問題を解く

県ごとの色は、数理最適化を用いて四色問題を解いて求めます。
数理最適化については、「[組合せ最適化を使おう](https://qiita.com/SaitoTsutomu/items/bfbf4c185ed7004b5721)」を参考にしてください。

最初、BlenderのPythonで実行したのですが、異常終了してしまったため、色の割り当て（変数`assign`の値）は下記のコードで別途求めました。

```py
from mip import Model

from japanmap import adjacent, get_data

qpqo = get_data()
m = Model()  # 数理モデル
v = m.add_var_tensor((47, 4), "v", var_type="I")
m += v.sum(axis=1) == 1
for ipr in range(47):
    for ad in adjacent(ipr + 1, qpqo):
        if ad > ipr:
            m += v[ipr] + v[ad - 1] <= 1
m.optimize()
assign = (v.astype(float) * range(4)).sum(axis=1).astype(int).tolist()
print(assign)
```

数理最適化は、[Python-MIP](https://www.python-mip.com/)ライブラリーを用いています。
どうも、BlenderとPython-MIPは相性が悪いようです。ちなみに、Blender2.83では動きましたが、Blender2.93以降は動きませんでした。

Python-MIPの使い方については下記を参考にしてください。

- [Python-MIPによるモデル作成方法 - PyQドキュメント](https://docs.pyq.jp/python/math_opt/python_mip.html)

ちなみに、「[Blenderで隣り合う面に異なる色を割当（四色問題）](https://qiita.com/SaitoTsutomu/items/8def3fb4b99f8520b6ed)」の貪欲法で色を求めると長野県のあたりで5色目を使ってたので、定式化して解くことにしました。

## やってみる

県の境界の座標や、県間の隣接関係は、japanmapというライブラリーを用います。
japanmapについては、「[県別データの可視化](https://qiita.com/SaitoTsutomu/items/6d17889ba47357e44131)」を参考にしてください。

Blender上で下記を実行すると、日本地図ができます。

```py
import bpy
import numpy as np
from mathutils import Vector

from japanmap import get_data, pref_names, pref_points

colors = [
    (1, 0.1, 0.1, 1),
    (0.1, 0.2, 1, 1),
    (0.8, 0.8, 0, 1),
    (0, 0.8, 0.1, 1),
]
center = np.array([139.75, 35.68])
assign = [
    0, 2, 0, 2, 1, 0, 3, 0, 2, 1, 3, 2, 0, 2, 2, 1, 0, 3, 1, 0, 2, 3, 1, 0,
    1, 2, 0, 1, 3, 2, 0, 1, 2, 3, 0, 1, 0, 3, 0, 0, 2, 1, 1, 3, 2, 0, 3,
]

for i, color in enumerate(colors):
    mat = bpy.data.materials.new(name=f"M{i}")
    mat.use_nodes = True
    mat.node_tree.nodes["Principled BSDF"].inputs["Base Color"].default_value = color
pnts = pref_points(get_data())
for ipr in range(47):
    verts = [Vector([*pt, 0]) for pt in (pnts[ipr] - center).tolist()]
    mesh = bpy.data.meshes.new(name=pref_names[ipr + 1])
    mesh.from_pydata(verts, [], np.arange(len(verts))[None, :])
    obj = bpy.data.objects.new(mesh.name, mesh)
    obj.active_material = bpy.data.materials.get(f"M{assign[ipr]}")
    bpy.context.scene.collection.objects.link(obj)
    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.mode_set(mode="EDIT")
    bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={"value": (0, 0, 0.2)})
    bpy.ops.transform.resize(value=(0.96, 0.96, 0.96))
    bpy.ops.object.mode_set(mode="OBJECT")
```

参考：[BlenderでPythonを実行する方法](https://qiita.com/SaitoTsutomu/items/cec67381a8789b40e377)

以上
Microsoft Sentinelに取り込んだOkta System Logの文字化けを修正する

Okta の System Log をMicrosoft Sentinelに取り込むとき、データコネクトのテンプレートが用意されているのでそれを使えば取り込むことが可能です。ですが、マルチバイト文字を取り込むと「????????」と文字化けしてしまいます。その場合の修正方法を記述します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29324/d3003f0a-76ad-65c5-3276-55f9cd38843f.png)

このデータコネクタを作成すると関数アプリで PowerShell が使用されます。その run.ps1 を以下のように修正することで文字化けが解消されます。
```powershell:run.ps1
# 128行目
- $responseObj = $response | ConvertFrom-Json
+ $responseObj = @($response.content) | ConvertFrom-Json
```
Cygwinのダウンロードとインストールを自動で行う方法


# 3行で説明

面倒なインストール、なんとかしたいですよね。
そこでscriptを書きました。コマンドプロンプトに貼ってENTERで5分～15分放置すればインストール完了です。
```bat
curl.exe -L https://raw.githubusercontent.com/cat2151/cygwin-auto-get-install/main/Cygwin_get_and_install.bat --output Cygwin_get_and_install.bat && Cygwin_get_and_install.bat
```

# この記事の対象読者
以下に興味のある方。何かの参考になれば幸いです。
- Cygwin
- C/C++ソースコードをコンパイル
- Windowsでgcc & clang
- ポータブル、環境を汚さない
- Cygwinのない環境でも実行できるexe（DLL依存しない）
- GitHubからcurlしたscriptで非対話的インストール

# このscriptは何を実現するの？
- 以下を自動化します :
  - Cygwinを公式サイトからダウンロードする
  - Cygwinをカレントディレクトリ配下にインストールする
    - レジストリやシステム環境変数に影響を与えません
    - 任意のディレクトリに移動しても動作します
  - Cygwinに(mingwの)gccとclangをインストールする
  - hello worldをコンパイルして実行する
    - Cygwinがない環境でも動作します（DLLに依存しません）
  - cygwin64起動batを生成する
    - このbatを実行するとcygwinにbashログインして(aliasによりmingwの)gccとclangが使えます
  - 上記すべてのログを出力する

- 環境を汚さないため、手軽に扱えます。
- コマンドプロンプトから1行のコマンドを実行するだけで自動ですべてが完了します。面倒な操作は不要です。

# 使っているパッケージマネージャーは？

[setup-x86_64.exe（最低限のpackage用）](https://www.cygwin.com/install.html)
[apt-cyg（大部分のpackage用）](https://github.com/kou1okada/apt-cyg)
[cpanm（perl用）](https://github.com/miyagawa/cpanminus/tree/devel/App-cpanminus)

# さらに詳しく
GitHubをご参照ください :
[Cygwin auto get install](https://github.com/cat2151/cygwin-auto-get-install)

# 関連記事
[MSYS2のダウンロードとインストールを自動で行う方法](https://qiita.com/cat2151/items/a93d2243b8eb62465a51)
[WSL2 + Docker + MinGWで、自動でhello worldをWindows用にクロスコンパイルする方法](https://qiita.com/cat2151/items/8ae0969204f1a38e1b4d)
仕様書にあるMUSTとSHOULD どう違う？ RFC2119を読んでみた

# MUST/SHOULDって何が違う？

さまざまな仕様書に、Must, shouldといったキーワードが出てくることがあります。そのニュアンスの違いがよくわからなかったので調べてみました。

# RFC2119

前に読んだOpenAPI specificationに、以下RFCへの参照がありました。
Must, shouldなどの用語が定義されています。

https://datatracker.ietf.org/doc/html/rfc2119

# まとめ

| Key word | 同義語 | 説明 |
|:-:|:-:|:-:|
| **MUST**  | REQUIRED, SHALL   | **必須**。仕様上、絶対やらなきゃダメ  |
| **MUST** **NOT**  | SHALL NOT  | **禁止**。仕様上、絶対やっちゃダメ。  |
| **SHOULD**  | RECOMMENDED  | **基本やるべき**だが、状況によっては正当な「やらない理由」がありえる。ただし仕様の意味するところを十分理解し、慎重に検討したうえで、他の方法をとること。  |
| **SHOULD** **NOT**  | NOT RECOMENDED  | **基本やらないべき**だが、状況によっては正当な「やる理由」がありえる。ただし仕様の意味するところを十分理解し、慎重に検討したうえで、他の方法をとること  |
| **MAY**  | OPTIONAL  | **本当にオプション**。やってもやらなくてもよい。ただし、このオプションが実装されたプロダクトと実装されていないプロダクトは、問題なく相互運用できなくてはいけない。  |

# 所感

SHOULDの仕様に対し、「MUSTじゃないのでやりませんでした。中身はちゃんと読んでいません。」というのは、RFC2119の定義に反していることを理解しました。
SHOULDの仕様を実装しないとき、もしくはSHOULD NOT仕様を実装する際には、十分検討して理由がきっちり説明できることが必要ですね。

また、MAYの仕様に対し、他の実装と相互作用する場合、実装されている場合いない場合両方に対応する必要がある、というのも要注意です。
実装されていないケースを想定しない実装は、やはりRFC2119の定義に反しているということです。
コツコツAtCoder (ABC218 B - qwerty)

AtCoderの過去問をコツコツ解くだけの記事です。
解いた問題全部を記事にするのは苦痛なので稀に気持ちが乗っている時だけやっていきます。

# 問題文
1以上26以下の整数からなる長さ 26 の数列 P=(P1,P2,P3,…,P26)が与えられます。ここで、Pの要素は相異なることが保証されます。
以下の条件を満たす長さ 26 の文字列 S を出力してください。
・任意の i(1≤i≤26)について、Sのi文字目は辞書順で小さい方からPi番目の英小文字である。
## 制約
1. 1≤Pi≤26
2. Pi≠Pj (i≠j)
3. 入力は全て整数である。

## 想定入力 / 出力
```:例①
入力 ： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
出力 ： abcdefghijklmnopqrstuvwxyz
```

```:例②
入力 ： 5 11 12 16 25 17 18 1 7 10 4 23 20 3 2 24 26 19 14 9 6 22 8 13 15 21
出力 ： eklpyqragjdwtcbxzsnifvhmou
```

# 回答
```C++:ABC218 B - qwerty
#include <iostream>
using namespace std;

int main() {
  int num = 97;
  int out[26];
  int p[26];
  
  for(int i=0; i<26; i++){
    cin >> p[i];
    out[i] = num+p[i]-1;
  }
  
  for(int l=0; l<26; l++){
    printf("%c",out[l]);
  }
  return 0;
}
```

**入力される数字に対応したアルファベットの値を出力する**という問題です。
これは、**入力された数字が"a"からどれだけ離れているか**を考えれば良いということになります。
例えば"b"は"a"から1文字分離れているので、距離は1。"e"は"a"から4文字文離れているので距離は4、みたいな感じです。

"a"は文字コードにすると0x61で、十進数にすると97です。
従って、**97からどれだけ離れているか**を計算して出力していけば問題なく回答することができます。
```:文字コードの例
a = 97
b = 98 (97+1)
C = 99 (97+2)
```

今回はそこそこシンプルに書いたつもりでしたが、公式の回答だと以下のようにさらにシンプルでかつ美しく記述されていました。
私はそもそも「**ans += (char)('a'+p);**」という記載方法を知らなかったので、C++の細かい文法を学ぶ機会も設けなきゃなぁと思いました（小学生並の感想）

```C++:公式回答
#include <bits/stdc++.h>
using namespace std;

int main(){
    string ans;
    for(int i = 0; i < 26; i++){
        int p; cin >> p; p--;
        ans += (char)('a'+p);
    }
    cout << ans << endl;
}
```

また、あまり美しくはないですが、アルファベットを26個格納した一次元配列を用意して、入力値-1を添字にして出力するという方法もあるかなぁと思いました。


ということで、アルゴリズムをもっと勉強していきます・・・
WSL2のインストール

# はじめに
windows 10からwindows 11にupdateしたので，これを機に，wsl2を使用してみることにした。

# 前提条件
- os: windows 11（windows10からのアップデート）
- windows10の状態でwslは導入済み

cmdでは下記の状態
``` dosbatch
C:\Users\XXXX>wsl -l -v
  NAME      STATE           VERSION
* Legacy    Running         1
```

# wslのインストールバージョンを2に変更
とりあえず，新しいディストリビューションの既定のインストールバージョンを2に変更する
`コマンド`
``` dosbatch
wsl --set-default-version 2
``` 
`結果`
``` dosbatch
C:\Users\XXXX>wsl --set-default-version 2
Windows の仮想マシン プラットフォーム機能を有効にして、BIOS で仮想化が有効になっていることを確認してく
ださい。
詳細については、https://aka.ms/wsl2-install を参照してください
```

ダメっぽい。
https://qiita.com/matarillo/items/98d7452967987fe5d633
を参考に，`windowsの機能の有効化と無効化`で，`仮想マシンプラットフォーム`を有効化

コマンド
``` dosbatch
wsl --set-default-version 2
```
結果
``` dosbatch
C:\Users\XXXX>wsl --set-default-version 2
WSL 2 との主な違いについては、https://aka.ms/wsl2 を参照してください
この操作を正しく終了しました。
```
成功

# Ubuntu-20.04をインストール
インストールできる有効なディストリビューションを確認する。
Ubuntu-20.04があった！
``` dosbatch
C:\Users\XXXX>wsl -l --online
インストールできる有効なディストリビューションの一覧を次に示します。
'wsl --install -d <Distro>' を使用してインストールします。

NAME            FRIENDLY NAME
Ubuntu          Ubuntu
Debian          Debian GNU/Linux
kali-linux      Kali Linux Rolling
openSUSE-42     openSUSE Leap 42
SLES-12         SUSE Linux Enterprise Server v12
Ubuntu-16.04    Ubuntu 16.04 LTS
Ubuntu-18.04    Ubuntu 18.04 LTS
Ubuntu-20.04    Ubuntu 20.04 LTS
```
インストールしてみる
コマンド
``` dosbatch
wsl --install -d Ubuntu-20.04
```
結果
``` dosbatch
C:\Users\XXXX>wsl --install -d Ubuntu-20.04
Ubuntu 20.04 LTS は既にインストールされています
Ubuntu 20.04 LTS を起動しています...

Installing, this may take a few minutes...
WslRegisterDistribution failed with error: 0x80370102
Please enable the Virtual Machine Platform Windows feature and ensure virtualization is enabled in the BIOS.
For information please visit https://aka.ms/enablevirtualization
Press any key to continue...
```
BIOSの設定をしてなかったようなので，PC再起動してBIOSの設定を変更
https://qiita.com/matarillo/items/98d7452967987fe5d633
を参考に

``` dosbatch
C:\Users\XXXX>wsl --install -d Ubuntu-20.04
```
エラー出ずに成功

## 補足
新規PCで上記を行った際に、インストール完了後、起動時にエラーが発生。（エラー画面はスクショ忘れ。。。）
https://aka.ms/wsl2kernel にいって、新しいkernel componentをインストールが必要になるみたい。
詳しくは、下記参照。
https://qiita.com/hali/items/bf04a1e4012025a38d6b
Vue.js 入門1 Hellow World

# 初めに
本記事はVue.js初学者向けの記事になります。
忘れっぽいので自分が学んだVue.jsのサンプルコードを記載していきます。
とりあえずCDNを使用した簡単な方法になります。

htmlファイルを作成し、下記のコードを記入してください。

```html:index.html
<!DOCTYPE html>
 <html lang="ja">
 <head>
  <meta charset="UTF-8">
  <title>Hellow World</title>
  <!--ここでVue.jsの読み込み-->
  <script src="https://unpkg.com/vue@next"></script>
 </head>

 <body>
   <div id="app1">
     app1: {{message}}
   </div>
   <div id="app2">
     app2: {{message}}
   </div>
   <script>
     const App1 = {
       data() {
         return {
           //変数名
           message: "Hello Word!"
         }
       },
     }

     const App2 = {
       data() {
         return {
           //変数名は分かれていれば
           message: "Hello Word!2"
         }
       },
     }

     app1 = Vue.createApp(App1)
     //上部のid=app1部分に表示させる事が出来ます。
     app1.mount('#app1')

     app2 = Vue.createApp(App2)
     app2.mount('#app2')
   </script>
 </body>
</html>
```
ブラウザで開くと下記の様に表示されます。
{{message}}が表示されてしまう場合はVue.jsの読み込みに失敗している可能性があります。

```
app1: Hello Word!
app2: Hello Word!2
```
【Python】Matplotlibの基本のキ

Pythonでグラフを描くためのライブラリーMatplotlibの基本的な使い方について説明します。
# 目次
[1.ライブラリのインポート](#ライブラリーのインポート)
[2.描画オブジェクトの設定](#描画オブジェクトの設定)
[3.タイトルの設定](#タイトルの設定)
[4.軸ラベルの設定](#軸ラベルの設定)
[5.凡例の設定](#凡例の設定)

# ライブラリのインポート
Matplotlibの描画モジュールpyplotをpltとしてインポートします。
JupyterNotebookなどを使っている場合、%matplotlib inlineを入れることによって、インライン（埋め込み）で表示できます。

```python:インポート
import matplotlib.pyplot as plt
%matplotlib inline
```


# 描画オブジェクトの設定
グラフを描画するための下準備について見ていきます。
描画オブジェクト（figure)という大きなキャンバスに、いくつかのサブプロット（ax)を配置し、サブプロットの中でグラフを配置していきます。
```python:描画オブジェクトの設定
figure,axes = plt.subplots(2,2) #2×2のサブプロットを配置
```
以下の通り2×2のサブブロットが配置されました
![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2118220/8af6ea8d-3a41-d0a7-37b1-d0a06f44bf06.png)
グラフの数が1つの場合は、subplots()と引数を渡さないでおけばOKです。


# タイトルの設定
サブプロット.set_title('タイトル名')で、サブプロットにタイトルを設定できます。
fig.tight_layout()はタイトルが軸ラベルと重ならないようにするためのものです。
```python:タイトルの設定
fig,axes =plt.subplots(2)
axes[0].set_title('subplot0')
axes[1].set_title('subplot1')

fig.tight_layout()
```
![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2118220/4ed5db42-21c5-a91f-3899-9e0f310e66f7.png)

# 軸ラベルの設定
サブプロット.set_xlabel('ラベル名')で、サブブロットのx軸にラベル名を設定できます。
y軸はset_ylabel（’ラベル名’）です
```python:軸ラベルの設定
fig,ax =plt.subplots()
ax.set_xlabel('x_label')
ax.set_ylabel('y_label');
```
![ダウンロード (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2118220/465bf06f-9331-da22-298a-5aa8fb4d0e01.png)


# 凡例の設定
引数label='凡例名'とし、ax.legend()を実行することで凡例を設定できます。legendは凡例という意味です。（伝説という意味もありますが）
```python:凡例の設定
fig, ax = plt.subplots()
ax.plot([1, 2, 3], label='legend 1')
ax.plot([3, 2, 1], label='legend 2')
ax.legend();
```
![ダウンロード (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2118220/16374a53-d52f-cbf4-bdad-ef580d5b47b4.png)
HarmonyOs3.0 体験

# HarmonyOs3.0体験（App開発ツール）
２０１９年５月から米国政府の禁輸措置よりファーウェイ製スマホ発売中止になって今の家電販売店にはノートｐｃ、モニター、ルーター、スマートウォッチのみ見えます。
２０１９年８月Androidを使用できなくなった場合に備えた「プランB」としたHarmonyOSを公表されて現在は「HarmonyOS 2.0」を既存のおよそ100機種についても、同OSが配信されることが発表された。
「HarmonyOS 3.0」は、２０２２年７月に正式にリリースされる予定です。最近、HarmonyOSユーザーの数がすでに2億2000万人を超えていることが発表されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551056/75367a6b-acdc-e01c-0e5d-aa91aee7b3fa.png)


中国製の世界３位のＯＳ（一位と２位はiosとandroid）に興味を持って試してみたいです。
- まずは開発ツールのダウンロードとインストール（[開発環境作成手順](https://developer.harmonyos.com/en/docs/documentation/doc-guides/installation_process-0000001071425528))
- 環境そろった後に「HelloWorld」を試してみました。([HelloWorld作成手順](https://developer.harmonyos.com/en/docs/documentation/doc-guides/hello_world-0000001054516888))
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551056/a5c95e5b-1b1b-a802-d9fd-f63e0189d2ea.png)
- 勉強するため安くなったスマホを一台購入する予定です。
![DCE9ADD7-6CD9-4139-B7CC-B2E6B1C1E27E.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551056/3910f972-9b2e-1764-af35-844b9f300da8.jpeg)
GitHubPagesを使ってサイトを公開しよう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/d0d0404f-6510-45af-923a-461fdf4ab909.png)

# GitHubPagesを使ってサイトを公開してみましょう！

https://pages.github.com/

# １，まず、GitHubに新規レポジトリを作成します！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/ab2f0400-11a3-e911-8008-12c131418cfb.png)

https://github.co.jp/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/cc728e40-4121-92b2-7fa4-103d0f46d2f1.png)

>①Repository nameに、レポジトリ名を記入します。
②Publicにチェックマークがついているか確認。
③Add a README file にチェックを入れる。
④Create repositoryボタンを押します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/9da366d2-8d41-727a-8230-8e6cfa184b90.png)

>新規レポジトリが作成されましたね!

# ２，GitHub Desktopにクローンします！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/a519042b-14ad-d4e3-4f7c-417f552a789d.png)


https://desktop.github.com/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/6b2dfa68-8956-d870-5aa7-963644d8a3c0.png)

>Clone a repository from the Internetから先ほど作成したリポジトリをクローンしてください。
リロードボタンを押すと、リポジトリが表示されます。
選択して、Cloneボタンを押しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/57e9e4b5-c6d6-89d4-9d48-7d5d255afc42.png)

>GitHub Desktopにクローンできました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/0ab2a36f-f46f-829a-ba7a-9bb3a11cb960.png)

>Repositoryをクリックして、Open in Visual Studio Codeで編集します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/ccaaaf2a-8183-5f07-fadb-f422be6e3765.png)

>index.htmlを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/a15290ba-490e-9145-4be6-81f35dacabf0.png)

>すると、GitHub Desktopが更新されます。
左下のCommit to mainをクリックします、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/ab9396ee-f587-23a2-8d86-bde7909db471.png)

>Commit to mainを押すと、右上にPush originが表示されますので、それをクリックすればGitHubにプッシュされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/b899ab00-ad82-bc33-c702-3b26090b9067.png)

>リポジトリが更新されていることがわかりますね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/f7de8fa0-5a2c-e558-0f71-a442b8bd1def.png)

>次に、Settingsに移動して、下にスクロールするとGitHub pagesのリンクが表示されています。
Check it out here!に移動しましょう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/42999a7b-0fd4-b211-0438-c128f7ebbff0.png)

>移動したら、Noneをmainに変更してSaveしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/14485db8-c8a4-60a9-f082-eeca145541ca.png)

>すると、リンクが生成されるので、そちらのリンクをクリックすると先ほど作成したindex.htmlが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/28ca56e6-3bd7-9288-87e1-c916e236eb37.png)

>表示されない場合は、少し時間をおいて再度リンクを押してください。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/e7fba89b-7716-aa5f-f9ed-99d1576e3f87.png)

# Deploy成功 :tada: :tada:

簡単に、GitHub pagesにDeployすることができましたね！


# 参考動画

https://youtu.be/QyFcl_Fba-k

https://youtu.be/o5g-lUuFgpg
vmware workstation playerを使う際に発生したエラー

タイトルにある通り VMware Workstation Playerを使う際に発生したエラーと対処法のメモが書いてあります。

「AMD-Vは無効になっています」
「Time out...EFI Network...」
の計2つのエラーが発生しました。


一つ目の`AMD-Vは無効になっています`
はBIOS/UFFIを作動させて「SVM Mode」をONにすることで解決しました。


二つ目の`Time out...EFI Network...`
はVirtual Machinesフォルダ>>(仮想マシンの名前)フォルダの中にある4KBの(仮想マシンの名前).vmxファイルを見つけます。そしてメモ帳でそのファイルを開きます。

その後、
```
firmware = "efi"
```
を
```
firmware = "bios"
```
に置き換えます。
保存は忘れないようにしましよう。
正規表現の /g フラグはサーバーで使うと怖いことがあるという話

まず、次のコードを見てください。

```
const http = require('http');
const url = require('url');


const regex = /^hoge$/g;
const server = http.createServer((req, res) => {
    if (regex.test('hoge')) {
        res.end('hoge', 'utf8')
    } else {
        res.end('hogeじゃない', 'utf8');
    }
});
server.listen(8000);
```
この Node.js 向けのコードを実行すると HTTP サーバーが起動します。
どんなレスポンスが返ってくるでしょうか？

```
% curl localhost:8000
hoge
% curl localhost:8000
hogeじゃない
% curl localhost:8000
hoge
% curl localhost:8000
hogeじゃない
% curl localhost:8000
hoge
% curl localhost:8000
hogeじゃない
```

1回ごとにレスポンスが変わります。
なぜこのようなレスポンスになるかというと、 **`regex` 変数に保持している正規表現のオブジェクトが状態を保持しているためです**。
以下、MDN からの引用です。

> 正規表現にグローバルフラグが設定されている場合、 test() は正規表現が所有する lastIndex の値を加算します。 (exec() も同様に lastIndex プロパティの値を加算します。)
> その後にさらに test(str) を呼び出すと、 str を lastIndex から検索します。 lastIndex プロパティは test() が true を返すたびに増え続けます。
>
>test() が false を返した場合、正規表現の lastIndex プロパティを呼び出すと 0 にリセットされます。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test#using_test_on_a_regex_with_the_global_flag

はじめのコードでは、`regex` 変数の正規表現のオブジェクトに `/g` とグローバルフラグが指定されているため、リクエストを受けるたびに `regex.test()` が実行され `lastIndex` が更新されます。
初回のリクエストでは `regex.lastIndex = 0` なので想定通りに `hoge` という文字列に正規表現がマッチします。
しかし 2 回目のリクエストでは `regex.lastIndex = 4` になっているため `hoge` の 4 文字目から `hoge` を探し始め正規表現がマッチしません。そして、マッチしないために `regex.lastIndex` が 0 に戻ります。


少しコードを書き足して確認してみると、以下のように lastIndex が 0 / 4 と切り替わっていることがわかります。
```
const http = require('http');
const url = require('url');

const regex = /^hoge$/g;
const server = http.createServer((req, res) => {
    if (regex.test('hoge')) {
        res.end(`hoge lastIndex: ${regex.lastIndex}`, 'utf8')
    } else {
        res.end(`hogeじゃない lastIndex: ${regex.lastIndex}`, 'utf8');
    }
});
server.listen(8000);
```

```
% curl localhost:8000
hogeじゃない lastIndex: 0
% curl localhost:8000
hoge lastIndex: 4
% curl localhost:8000
hogeじゃない lastIndex: 0
% curl localhost:8000
hoge lastIndex: 4
```

常に同じ判定をさせるためには、正規表現の後ろの `/g` で指定している global フラグを使用しない、lastIndex を実行のたびに 0 に戻す、正規表現オブジェクトのスコープを絞るなどの対策が必要です。

以下のように global フラグをはずすだけで常に同じレスポンスを返すようになります。

```
const http = require('http');
const url = require('url');


const regex = /^hoge$/;
const server = http.createServer((req, res) => {
    if (regex.test('hoge')) {
        res.end(`hoge lastIndex: ${regex.lastIndex}`, 'utf8')
    } else {
        res.end(`hogeじゃない lastIndex: ${regex.lastIndex}`, 'utf8');
    }
});
server.listen(8000);
```

```
% curl localhost:8000
hoge lastIndex: 0
% curl localhost:8000
hoge lastIndex: 0
% curl localhost:8000
hoge lastIndex: 0
% curl localhost:8000
hoge lastIndex: 0
% curl localhost:8000
hoge lastIndex: 0
```

というわけで、正規表現オブジェクトは状態を持つので気をつけないといけないよ、ということを最近改めて思い出したのでメモを残しておきます。
docker-compose が stdout を拾ってくれない問題

docker-compose でコンテナ起動した際に、dockerがコンテナ側の標準出力を拾ってくれなくなり困ったのでメモを残しておく。

#### 現象
`docker-compose up` で起動した後、 `Attaching to ****` 以降のログが表示されなくなった。
コンテナは正常に稼働していたので、起動に失敗しているわけではないらしい。
使用環境はmac os(10.15.7)で、使用していたイメージは golang。

#### 解決
Docker Desktop for Mac をダウングレードすることで解決した。
4.6.0 → 4.4.2

##### Dockerfileとdocker-compose.yml
おそらく今回の問題とは関係ないと思うけど。

```Dockerfile:Dockerfile
FROM golang:1.17.8-bullseye

ENV GOPATH /go
ENV GO111MODULE on

RUN mkdir /go/src/app
WORKDIR /go/src/app

ADD . /go/src/app

RUN go mod init v1
RUN go mod tidy
RUN go install github.com/cosmtrek/air@v1.29.0
```

```yml:docker-compose.yml
version: '3'
services:
  go:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 8081:8080
    volumes:
      - ./app:/go/src/app:delegated
    tty: true
    stdin_open: true
    command: air -c .air.toml
```