OmniAICreator/Qiita-1.07M huggingface dataset:
再利用可能なワークフローを使ってみた

## まえがき
私の所属するチームではGitHub Actionsを使った各種自動化を実施しています。  
Pull Requestの作成やブランチへのコミットなどに反応して、ユニットテストやLinterなどが実行されたり、アプリケーションがデプロイされるようになっています。  
これによってレビューの手間が簡略化されたり、Pull Requestをリリースブランチにマージするだけでリリース作業を完結させられたりしています。  

その一方で、年々CI/CDでやりたいことが増え、ワークフローの定義ファイルがどんどん長くなっていき、可読性やメンテナンス性が落ちていくことに悩んでいました。  
今回はその悩みの解決のため、再利用可能なワークフローを活用してワークフローを分割したので、それについて書こうと思います。

## 抱えていた課題
私がメインで開発しているリポジトリには、  

1. テストジョブ
    1. シンタックスチェック
        1. バックエンドのシンタックスチェック
        1. フロントエンドのシンタックスチェック
    1. テストのためのビルド
    1. バックエンドのユニットテスト
    1. フロントエンドのユニットテスト
1. デプロイジョブ
    1. (リリースブランチで発火した場合のみ)デプロイ

の処理を一気に行う巨大なワークフローが存在していました。  

ひとつのファイルにまとまっていることで「ここを読めばなんとかなる」という状態ではあったものの、初めて触るメンバーにとっては敷居が高く、メンテナが固定化されてしまっていました。  
また、これはワークフローの問題ではないのですが、一部テストコードがflakyなものになっており、ときおり失敗してはワークフローをすべて再実行するため十数分待ちということも発生していました。  

そこで、再利用可能なワークフローを使って処理ごとにワークフローを分割し、

- 失敗した箇所のみを再実行できるようにする
- 各種テストを並列実行できるようにする
- コード変更の影響範囲を狭めて、触りやすくする

ことを試すことにしました。  

## 再利用可能なワークフローとは
再利用可能なワークフローとは、他のワークフローから呼び出し可能なワークフローです。  
使い始めるのは簡単で、まず、再利用したいワークフローの発火条件を以下のようにします。  

```.github/workflows/reusable.yaml
name: 再利用される側のワークフロー

on:
  workflow_call:
# ほかにも発火条件を足したければ、普通に書く

# 以下、いつも通りにジョブやステップを書く
```

その後、呼び出し元のワークフローで、再利用したいワークフローのファイルパスを指定することで、呼び出せます。  

```.github/workflows/main.yaml
# 省略
jobs:
  reuse:
    uses: ./github/workflows/reusable.yaml
```

似たものとして、[カスタムアクション](https://docs.github.com/ja/actions/creating-actions/about-custom-actions)があります。  
カスタムアクションとの大きな違いは、  

- ワークフローを置いておくリポジトリを分ける必要がない
- パッケージとして公開する必要がない

の2点になります。  
詳細については、[公式ドキュメント](https://docs.github.com/ja/actions/using-workflows/reusing-workflows)を参照してください。  

## どう分けたか

今回のケースで、私はメインのワークフローから  

- ビルド
- シンタックスチェック
- フロントエンドのテスト
- バックエンドのテスト

を再利用可能なワークフローとして切り出し、  

- PR作成時・PRへのコミット時に発火するテスト系のワークフロー
- リリースブランチへのマージ・コミット時に発火するデプロイ系のワークフロー

のそれぞれで、必要なものを呼び出すように変更を加えました。  
最終的なファイル構成は以下のようになります。  

```zsh
.github
└── workflows
   ├── deploy.yml
   ├── integration.yml
   ├── syntax-check.yml
   ├── build.yml
   ├── backend-test.yml
   ├── frontend-test.yml
   ├── etc...
```

基本的には通常のワークフローをそのまま分けていけば動くので、再利用可能にすること自体はとても簡単です。  


## 分割したまとめ
分割してしばらく経ってからこの記事を書いています。  
経過を見た感想としては、以下のよかったこと、いまいちだったことがあると感じています。  

### よかったこと
- ワークフローの依存関係が整理された
- 失敗時の再実行性が改善された
- ワークフローを小分けにしたことで、ここだけならちょっと触ってみようかな？ という人が出てきた
- 新規のワークフローを書くときに再利用性・再実行性を意識するようになった

### いまいちだったこと
- カスタムアクションEOL対応の影響範囲が増えてしまった
- 60秒未満のワークフローは60秒とみなされてしまうため、課金対象の実行時間が増えてしまう
  - 60秒以上になるように処理をまとめた箇所がある

再利用可能なワークフローはメリットも大きいですが、メンテナンス・課金回りでの影響・手間も発生してしまいます。  
そのため、全員に手放しでおすすめというわけにはいきませんが、再実行性や見通しの悪さに悩まされている方は検討してみてはいかがでしょうか。
【TypeScript】オブジェクト型を作成する方法

`Record<Keys, Type>`を使用するとキーがKeysであり、プロパティの値がTypeであるオブジェクトの型を作成できます。

```ts
type Object = Record<string, number>
const value: Object = { a: 1, b: 2, c: 3 }

// キーが文字列で値は不明なオブジェクト
type Object = Record<string, unknown>
```
React公式ページで学んでみた⑤（備忘録）

# はじめに
初めまして！
エンジニアになって数年、今まで本を読むだけでしたが、もっとプライベートで楽しみながら成長したい！自分が学んだ足跡を残していきたい！と思い記事を書きました！
最終的には自在に開発できるようになりたいと思っています。:triumph:
いろいろな記事を参考にさせてもらっています。:bow_tone2:
その中でもこれってどういう意味？とかつまづいたところを念入りに書いていこうかと思います。:fist:

# 今回の目的
前回の続きです。今回も「インタラクティビティの追加」。被る部分もあると思いますが、後で振りかえって深掘りするきっかけになるのが理想です。細かく書くと大変なのであっさりめ。

https://qiita.com/mr_noppo/items/4e621111f6b26f563c62

# 使用したものや事前準備
・Macbook Pro
・Visual Studio Code

# 主に参考にさせていただいた記事
https://ja.react.dev/learn/state-as-a-snapshot

# stateについて
stateは変数ではなくスナップショットのように振る舞う。
スナップショットになぜ例えるかというと、state自体は別に管理しており、set関数が動いた時に返すからである。
例えば、下記のような記述だとレンダー時には、全て同じ値を指している（スナップショット）ため＋３インクリメントすることはない。
次回のレンダーでnumber+1の準備を３回するだけである。

```react
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1); //初期レンダー時のnumberは全て0である
        setNumber(number + 1);
        setNumber(number + 1);
      }}>+3</button>
    </>
  )
}
```

同時に言えることは、イベントハンドラ内のset関数はバッチ処理されて一回のレンダーにまとめられているということ。（効率的）
また、他の関数も同じバッチ処理としてまとめられている。ただし、関数が発火するところまでしか監視していないので例えば、setTimeout内で3秒後にset関数を動かすとしてもそれは別のレンダーとして処理される。また、渡されるstateもイベントハンドラが発火した時点のキャプチャになる。
下記の例では、オンクリック発火時、イベントハンドラ内のアラートは0だが、イベントハンドラ外の再レンダー後のためアラートは5になる。
```react
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
        {alert(number)} {/*こっちは5*/}
      <button onClick={() => {
        setNumber(number + 5);
        alert(number); //こっちは0
      }}>+5</button>
    </>
  )
}
```

# 終わりに
イベントハンドラ内のset関数による再レンダーはまとめて行われること。また、JSX内のstateはその時点のキャプチャであり、レンダーが走らない限りset関数をいくつも使っても変化しないことがわかりました。
ここら辺は結構混乱しそうですが、プログラミングを学び始めた時にはよくあることなのでなれて行きたい思います。

# 次
https://qiita.com/mr_noppo/items/66ffe2d745edf578fed7
CentOS8 Gitlab オフライン構築方法

前提
・CentOS8が仮想マシンにてインストールされていること。
・SELINUXとfirewallを無効化していること。
・仮想マシンのユーザー名：パスワードはroot：rootとする。
・本手順書では仮想マシンのIPアドレスを192.168.56.20とする。

目次
1.gitlabのrpmファイルダウンロード(インターネット環境)
2.CentOS8のイメージファイルとgitlabのrpmをCentOS8仮想マシンへ送信
3.CentOS8のイメージファイルをマウントする
4.repoファイルの追加
5.perlのインストール
6.gitlabのインストール
7.稼働確認

### 1.gitlabのrpmファイルダウンロード(インターネット環境)
以下のサイトより、gitlabのrpmファイルをダウンロードする。
※本手順では、バージョン15.10.2をダウンロード
https://packages.gitlab.com/gitlab/gitlab-ce?filter=rpms

### 2.CentOS8のイメージファイルとgitlabのrpmをCentOS8仮想マシンへ送信
以下のコマンドにより、仮想マシンのCentOS8にファイルを送信する。
```
scp gitlab-ce-15.10.2-ce.0.el8.x86_64.rpm root@192.168.56.20:/home
scp CentOS-Stream-8-x86_64-latest-dvd1.iso root@192.168.56.20:/home
```
（仮想マシンCentOS8　/home上のファイル）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760232/65ca3e84-6da8-04f7-699c-fc0e9bae072f.png)

### 3.CentOS8のイメージファイルをマウントする
```
cd /home
mount CentOS-Stream-8-x86_64-latest-dvd1.iso /mnt
```

### 4.repoファイルの追加
CentOS8のイメージファイルからrpmをインストールするため、
以下のコマンドにより、repoを追加する。
```
vi /etc/yum.repos.d/InstallMedia.repo
```
ファイル内容は以下を記載して保存
```
[InstallMedia-BaseOS]
name=centos
metadata_expre=-1
gpgcheck=0
enabled=1
baseurl=file:///mnt/BaseOS/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
[InstallMedia-AppStream]
name=centos
metadata_expre=-1
gpgcheck=0
enabled=1
baseurl=file:///mnt/AppStream/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
```
### 5.perlのインストール
以下のコマンドを実行してイメージファイルからperlをインストールする。
```
dnf install --disablerepo=\* --enablerepo=InstallMedia-* perl
```


### 6.gitlabのインストール
以下のコマンドを実行することで、gitlabが起動したときの最初のアクセスＵＲＬを指定してgitlabをインストールする。
```
 sudo EXTERNAL_URL="http://192.168.56.20/gitlab" rpm -i gitlab-ce-15.10.2-ce.0.el8.x86_64.rpm
```

以下の画面となれば、インストールして起動が完了。
（上記コマンドにより、インストールと起動を同時に行う。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760232/e6d47fb0-7beb-10df-50bb-55e54631a147.png)

### 7.稼働確認
6で指定したURLでアクセスする。（http://192.168.56.20/gitlab）
ログイン画面が表示されるため、etc/gitlab/initial_root_passwordを確認し、rootでログイン可能。

以上。
GitのBranchに対して抱いていた誤解 + ファイルを書き換えるgit command一覧

# はじめに

業務でGitを触る中で、これまでぼんやりと理解したつもりになっていた部分や誤解していた部分が最近はっきりしました。
以下では、その中身と関連して、ファイルに変更を加えるgit command一覧についてまとめました。
初心者向けに、このcommandを使わなければ、変更内容が消えたりすることはない。という視点でのまとめです。

# 結論
- ブランチによるバージョン管理は、別々の世界線を設けているのではなく、同じ世界線を別視点で見ているだけ

# Gitのバージョン管理について

GItはバージョン管理ツールです。
Gitの謳い文句として、
「文章_A.txt, 文章_B.txtのようにコピーを作成するバージョン管理から脱却しよう」
というようなことが言われると思いますが、まずはこれについて詳しく書いていきます。

### 具体的にどういうふうに管理しているか

具体的な手順です。
1. ファイル作成、保存
2. git addで記録したい内容を選択
3. git commitで選択した内容を記録

行ったcommitは、commit hashという数字とアルファベットの組み合わせで名前がついていて、その名前でいつの変更かを管理しています。

### 本題：ブランチについて

branchとは、厳密に言うとこのコミットのあとにこのコミット、次にこのコミットという変更履歴を記録したもの。
ブランチはファイルを持っていません。

そのため、ブランチを変更しても、同じディレクトリを見ている限り、ファイル内容は同じです。

ブランチAとブランチBで違う変更作業を行っていたとして、ブランチAでファイルを変更しctrl+Sで保存、ブランチBに移動して同じファイルを変更してctrl+Sで保存したとします。
ここで再度ブランチAに戻ってたとしても、ブランチAでの変更時点には戻りません。

これはctrl+Sでなく、git commitしても、git checkoutでブランチの移動を行うだけではファイルの変更は起こりません。もとの変更内容を手元に持ってきたければ、ブランチの移動をするたび、毎度pullする必要があります。

# ファイルに変更を加えるgit command一覧

ブランチの変更だけでは、ファイル内容はもとに戻らないということで、以下では、ファイルに変更が加わるgit commandをまとめていきます。

### git pull

最新のコミット内容との差分をファイルに反映させる

### git checkout ファイル名

ファイルの変更内容を捨てて、最新のcommitの状態に戻す。

### git clone ~

git repositoryを、ディレクトリ内にダウンロードします。

### git reset

git checkoutファイル名が、ファイル単体をもとに戻すのに対し、管理しているファイル全体をもとに戻します。

### git clean

バージョン管理されていないファイルを削除。

使うものでこれくらいだと思います。他にあれば追記します。
CSVダウンロード自分用メモ



```js

  const hogeList = [
    {
      name: "malp",
      addres: "top",
      number: "333",
    },
    {
      name: "yasuo",
      addres: "mid",
      number: "222",
    },
    {
      name: "taro",
      addres: "adc",
      number: "111",
    },
  ]

  const hogeSort = "num"

  hogeList.sort((a, b) => a[hogeSort] - b[hogeSort])

  console.log(hogeList)


  /** Object の配列を受け取り CSV形式の文字列に変換する Func */
  const convertToCSV = (objArray) => {
    const array = typeof objArray !== "object" ? JSON.parse(objArray) : objArray;

    /** 1. Objectの Key を headerとして取り出す */
    let str =
      `${Object.keys(array[0])
        .map((value) => `"${value}"`)
        .join(",")}` + "\r\n";

    // 2. 各オブジェクトの値をCSVの行として追加する
    return array.reduce((str, next) => {
      str +=
        `${Object.values(next)
          .map((value) => `"${value}"`)
          .join(",")}` + "\r\n";
      return str;
    }, str);
  };

  /** Download・処理 */
  const downloadCSV = (data, name) => {
    /** Blob Object を作成する Type. CSV */
    const blob = new Blob([data], { type: "text/csv" });
    console.log("blob", blob);
    const url = window.URL.createObjectURL(blob);
    console.log("url", url);
    const a = document.createElement("a");
    a.setAttribute("href", url);
    a.setAttribute("download", `${name}.csv`);
    a.click();
    a.remove();
  };

  /** CSVデータを作成 */
  const csvData = convertToCSV(hogeList);
  console.log(csvData);

  // CSV・Download
  downloadCSV(csvData, "hogeCsvData");
  ```
Linux勉強１日目：dnf updateがCurl error (6)で失敗したのでDNS設定を修正した。

LPICレベル１取得を目指して勉強することにした。

CentOSインストールだーと思ったらサポート終了していたらしい。５年前の俺の知識が古いという事実が襲いかかる。

ということでRockyLinuxをインストール。
まずはパッケージの更新をする。

yumだーと思ったら最近はyumじゃなくてdnfを使うらしい。５年前の俺の知識が云々。

```
su -
```
でrootにログインし、dnfコマンドを実行する。
```
[root@localhost ~]# dnf update
```
そしたらエラーが発生した。
```
Curl error (6): Couldn't resolve host name for https://mirrors.rock
　　　　　　　　　9 [Could not resolve host: mirrors.rockylinux.org]
```
どうやら仮想マシンのDNS設定がうまくないっぽい。
/etc/resolve.confでDNSの設定を修正する。

/etcにはLinuxの設定ファイルがたくさん入っているみたい。

```
[root@localhost ~]#  vi /etc/resolv.conf

nameserver 8.8.8.8
```

設定が終わったらもう一回

```
[root@localhost ~]#  dnf update 
```

正常終了した。
モンテカルロ積分を実装する

前回のモンテカルロ法を用いた円周率の計算に続いて、今回はモンテカルロ法で積分を計算します。動作環境はVisual Studio Community 2022のバージョン17.7.4です。使用言語はC++20です。

# 理論
次の式の積分について考えます。

```math
P = \int_{a}^{b}f(x)dx
```

モンテカルロ積分では、積分区間$[a, b]$において、確率密度関数$p(z)$の一様分布に従うN個の乱数$z_1,z_2,z_3....,z_N$を発生させます。この時、関数$f(x)$の期待値$I$は以下の式で近似できます。
```math
I = \int_{a}^{b}f(x)p(x)dx \approx \frac{1}{N}\sum_{k=1}^{N}f(z_k)
```
ここで区間$[a, b]$における一様分布の確率密度関数$p(z)$は次の式で表されます。
```math
p(x) = \frac{1}{b-a}
```
よって
```math
I = \int_{a}^{b}f(x)\frac{1}{b-a}dx \approx \frac{1}{N}\sum_{k=1}^{N}f(z_k)
```
となるから

```math
P = \int_{a}^{b}f(x)dx \approx \frac{b-a}{N}\sum_{k=1}^{N}f(z_k)
```
と近似計算できます。

# 実装
モンテカルロ積分を使って$\int_{0}^{\pi}sin(x)dx$を計算するコードを以下に示します。

```C++:Montecarlo.cpp
#include <iostream>
#include <random>
#include <numbers>

int N = 1000000; //乱数の発生個数

double f(double x)
{
	return std::sin(x); //積分対象の関数を定義
}

double montecarlo(double a, double b)
{
	std::random_device rng;
	std::uniform_real_distribution<> dist(a, b); //a以上b未満の一様分布を定義
	double m = 0;

	for (int i = 0; i < N; i++)
	{
		m += f(dist(rng)); //シグマの中の計算
	}

	return m * (b - a) / N;
}

int main()
{
	std::cout << montecarlo(0, std::numbers::pi) << std::endl; //0からπで積分する
}
```

実行する度に値は変わるが、ほとんどの場合で$\int_{0}^{\pi}sin(x)dx=\left[ -cos(x) \right]_0^\pi=2$より、$2$付近の値が出力されます。

# 参考
https://www.kspub.co.jp/book/detail/5279786.html
https://disassemble-channel.com/monte-carlo-integral/
Nest.jsでドメイン駆動開発をする場合のディレクトリ構成を考えてみる

## 概要

Nest.jsを使ったドメイン駆動開発をするに当たり、両方について学ぶ必要があった。
ディレクトリ構成にすればいいか考えることで、理解が深まったため、現状の認識を覚えておく意味でもメモを残しておく（間違っているかもしれないので、それも含めて）
プロジェクトの規模などによって、うまくメリットを享受できるように調整すべきなので、千差万別で参考にならないかとは思っています。そこが難しいところ

## アーキテクチャ

オニオンやらヘキサゴナルやらクリーンやらありますが、ざっくり下記を想定。

### ドメイン層

ビジネスロジックを実装する

### アプリケーション層

ユースケースを実装する

### プレゼンテーション層

MVCでいうコントローラーを実装する（Nest.jsの機能をそのまま使う）

### インフラ層

データベースやほかサービスなどとの通信周りを実装する

## 構成

```
/src
    /@core プレゼンテーション以外を閉じ込める
        /domain
            /entity
                some.entity.ts
                some.value-object.ts
                some.aggregate.ts
            /service
                some.service.ts
                unit-of-work.ts トランザクションのインターフェース 場所難しい
            /repository
                user.repositoty.ts
        /application
            /user
                create-user.use-case.ts
                create-user.input.ts
                create-user.output.ts レスポンスの形式（ここは微妙かも）
        /infra
            /prisma
                /migrations
                /seed
                /repository
                    user.prisma.repository.ts
                    unit-of-work.prisma.ts
                prisma.service.ts
                prisma.module.ts
            /redis
            /stripe
    /user
            /dto
                create-user.dto.ts
            user.controller.ts
            user.module.ts
    /common
        /middlewares
        /guards
        /pipes
        /intercepters
        /pipes
    /job
        /some-batch nest-commanderで実行するバッジなど？
```
CPUとメモリの監視ログ

### 概要
性能試験をやっている時に、vmstatコマンドを使用してCPUとメモリの使用率を測定していました。
CPUとメモリの使用率を測定しながら、各値の判定をできないかと思いスクリプトを作成してみました。
本来はLinux環境で実行できるようにしたいのですが、環境が用意できなかったため、Windowsで実行できるようにPowerShellで作成しました。


### スクリプト内容
CPUとメモリの使用率が定期的に監視するスクリプトです。
指定された条件が満たされた場合にアラートが表示されます。

CPU使用率を取得する関数、メモリ使用率を取得する関数、ログにデータを追加する関数を作成して、ループ処理で実行するようにしました。
約2秒間隔で監視するようにしています。


### スクリプト
```
# 監視ログファイルのパス
$LOG_FILE = "monitoring_log.txt"

# アラートの閾値
$CPU_THRESHOLD = 100    # CPUが100%になった場合の閾値
$CPU_AVERAGE_THRESHOLD = 60    # CPUの平均使用率が60%を超えた場合の閾値
$MEMORY_AVERAGE_THRESHOLD = 50 # メモリの平均使用率が50%を超えた場合の閾値

# ログにデータを追加する関数
function Log-Data {
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    Add-Content -Path $LOG_FILE -Value "$timestamp - CPU: $args[0]%, Memory: $args[1]%"
}

# メインの監視ループ
while ($true) {
    $cpu = Get-WmiObject Win32_PerfFormattedData_PerfOS_Processor | Select-Object -ExpandProperty PercentProcessorTime
    $cpu_usage = [math]::Round(($cpu | Measure-Object -Average).Average, 2)

    $memory = Get-WmiObject Win32_OperatingSystem | Select-Object -ExpandProperty FreePhysicalMemory
    $total_memory = Get-WmiObject Win32_OperatingSystem | Select-Object -ExpandProperty TotalVisibleMemorySize
    $memory_usage = [math]::Round((($total_memory - $memory) / $total_memory) * 100, 2)

    Log-Data $cpu_usage $memory_usage

    # CPUが100%の状態が20秒続いた場合、平均でCPU使用率が60%を超えた場合、メモリ使用率が50%を超えた場合にアラートを表示
    if ($cpu_usage -ge $CPU_THRESHOLD) {
        Write-Host "CPU is at 100% for 20 seconds."
    }
    elseif ($cpu_usage -ge $CPU_AVERAGE_THRESHOLD) {
        Write-Host "Average CPU usage is above 60%."
    }
    elseif ($memory_usage -ge $MEMORY_AVERAGE_THRESHOLD) {
        Write-Host "Average memory usage is above 50%."
    }

    Start-Sleep -Seconds 1 # 1秒待機してから再度監視
}

```

### 実行結果
PowerShellの起動画面には、以下のように出力されます。
```
Average memory usage is above 50%.
Average memory usage is above 50%.
Average memory usage is above 50%.
```

ログファイルの出力内容
```
2024-03-31 21:25:43 - CPU: 1.31 61.04[0]%, Memory: 1.31 61.04[1]%
2024-03-31 21:25:44 - CPU: 8.54 61.4[0]%, Memory: 8.54 61.4[1]%
2024-03-31 21:25:46 - CPU: 6.54 61.45[0]%, Memory: 6.54 61.45[1]%
2024-03-31 21:25:47 - CPU: 9.92 61.36[0]%, Memory: 9.92 61.36[1]%
2024-03-31 21:25:49 - CPU: 7.69 61.2[0]%, Memory: 7.69 61.2[1]%
```

### 結果
CPUとメモリの使用率を監視するログファイルを取得できるところまではできました。
改善点は測定の間隔をsleapで管理しているため、ログファイルを見ると出力時間が一定間隔ではありませんでした。別の方法で管理するか修正が必要です。

次はLinuxで実行できるものを作成しようと思います。
タスク管理アプリ作成①　〜環境構築〜

### はじめに

今回、学習用に「タスク管理アプリ」を作成することになったので、Laravelをローカルで動作させる環境を作成することがこの記事の目的です。


### 使用言語や環境

▼　ローカル環境
・Composer
→ homebrew使用しインストール済

・Laravel
→ Composer使用し、タスク管理アプリ用のプロジェクトを作成

▼ 本番環境（ここは考え中なのでメモ）
・AWS
・ドメイン
・DNSサーバー
・WEBサーバー

### 環境構築手順
#### ① Laravelのプロジェクト作成
※composerをhomebrew経由でインストールしていたが、2023年11月にphp8.0のサポートを終了したらしい。brew updateしたらcomposerが動かなくなった。なので、php8.2をインストールしてPATHの指定・変更反映をしたら無事動くようになった。

```
// brewコマンドでComposerをインストール
brew install composer
```

参考記事↓
https://qiita.com/Sxun0325/items/6c2f06b906e83b2dfe91

```
// プロジェクトを配置するディレクトリに移動
cd プロジェクト配置箇所

// composerを使用してlaravelプロジェクトを作成する
composer create-project --prefer-dist laravel/laravel プロジェクト名
```

#### ② laravel起動

```
// artisanコマンドでビルドインサーバーを起動
php artisan serve 
```
実行で表示されるURLにアクセスすればローカルにLaravelのトップページが表示されます。

![スクリーンショット 2024-03-31 23.18.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3513569/612c3ea1-239a-ce09-c944-c37e3aa38477.png)

これで、ローカルで開発を行って確認することができるようになりました。
次はログイン機能とダッシュボードで使用する機能の作成を行っていきます。
Arduino CloudでガーデニングIoT -自動灌水その1-

## 自動灌水

最終的には、土壌・植物・季節に応じて量と頻度を調節する自動灌水のシステムを作り、スマホで植物の状態を確認できるシステムを作りたい。まずは、土壌の水分量をセンシングする部分をESPr® DeveloperとArduino Cloudを用いて作成した。


## 準備物（¥4000程度）

- [ESPr® Developer（ESP-WROOM-02開発ボード）](https://www.switch-science.com/products/2500)
- ピンヘッダー（2.54 mmピッチ)
- [土壌水分センサー](https://www.amazon.co.jp/DiyStudio-湿度検出モジュール-アナログ出力土壌水分リアルタイムデータ-耐食性ケーブル付き-3-3〜5-5Vワイド電圧配線容量性センシングの原理を使用して土壌水分を検出します（5/dp/B07MC9ZGXL/ref=sr_1_3_sspa?__mk_ja_JP=カタカナ&crid=2GO4623IPPGET&dib=eyJ2IjoiMSJ9.IFgN_XSwKRbzoQSGshcrT_KIlKb2bDUcdomq6izqzIaN4_LalEESH0qaMr8e8PZVw1GV6qqk_bJLpSBpbTE51XiHvGbPZBLriBJex3fNGdKyjL5GatvlAb9RtiYtXg9Gi0u1Z9REqa2m_7t_guXTJZiZWSFwrKvPicQeokuhc88VmVRgj6GCULnnt4MGDYHg-HaiUhX2qsWvzzzU4z0FP7ONmoG550GPcHeLCdUgeAMcbrAXeVgcjej0EBOOywQ3YjqqoUCA9M4YwvZulYiiYfasuCGAPrikXBFUGu62OHU.J_nylRqQe7EbWMdOk1uqX8xXmERieND7DHNLN88Q5c8&dib_tag=se&keywords=土壌水分センサー&qid=1711805836&sprefix=土壌水分センサー%2Caps%2C178&sr=8-3-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1)
- ブレッドボード
- ジャンパー線
- USB mini-Bケーブル


手持ちのArduinoを活用しようと思ったが、Wi-Fi対応でなかったため、廉価版Arduinoとして利用できるESPr® Developerを購入。土壌水分センサーはAmazonで売っているセンサーを買った。また、下記を参考に、ESPr® DeveloperにはGeneric ESP8266 Moduleをインストールしておいた。

https://sample.msr-r.net/esp8266-setting/


## Arduino Cloud

とりあえず土壌水分センサーの出力をそのままアナログ入力に繋いでみて、[Arduino Cloud](
https://cloud.arduino.cc/)と繋げる。


![名称未設定のノート-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3756622/740ab572-cd0c-5665-cb0b-638a4b8930eb.jpeg)

基本的に以下の記事を参考に進めれば問題なく接続できる。

https://qiita.com/macole/items/ba3fcbcc5945423984ac

ただし、deviceの登録時には"Third party device"を選択する。

![スクリーンショット 2024-03-30 23.30.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3756622/b7bde837-1d5a-951c-b5be-71c91d43165d.png)

セットアップでのクラウド変数とスケッチについては以下の通り設定して、USBに接続した。"cloudMoisture"は既にセットアップで定義しているので、スケッチでは型指定しない。スケッチを書き込んだらESPr® DeveloperをPCに繋ぐ必要はなく、Wi-Fi経由で状態を確認したり、ダッシュボードの設定を行うことができる。
※macでは、Safariで書き込みができず、Chromeでは書き込むことができた。

![スクリーンショット 2024-03-30 23.57.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3756622/cd365c41-20b4-aa70-c096-db78d4f12b9c.png)

```
#include "thingProperties.h"

void setup() {
  Serial.begin(115200);

  delay(1500); 

  initProperties();

  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
}

void loop() {
  ArduinoCloud.update();  
  cloudMoisture = analogRead(A0);
}
```
次に、ダッシュボードの設定を行う。"Chart"を選び、"Linked Variable"に先ほど設定したクラウド変数"cloudMoisture"を紐付ける。すると、入力を時系列データとして表示することができ、無料のFreeプランでは過去1日間のデータを表示することができる。csv形式でダウンロードすることも可能。

![スクリーンショット 2024-03-31 22.46.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3756622/a743e2fd-1047-f062-ac97-7bcd7603deb3.png)

今回使用したボードとセンサーでは入出力の範囲が噛み合わず、とりあえず抵抗分圧でアナログ入力の電圧を1/3にした。次はこのセンサーを入力に繋ぐ最適な方法を探していく。

## 参考

https://sample.msr-r.net/esp8266-setting/
https://cloud.arduino.cc/
https://qiita.com/macole/items/ba3fcbcc5945423984ac
【AWS】AWS CLIに新しいStackSets APIが追加されました

# はじめに
AWS Cloudformation StackSetsにおいて、スタックセットに対して自動デプロイを設定したターゲット組織単位(OU)とAWSリージョンがAWS CLIで一覧表示できるようになりました。以前は特定OUの個別アカウントにログインして確認する必要がありましたが、その必要がなくなりました。


# 使ってみた
デフォルトではjsonで出力されるのですが、組織やリージョン数が多くなるとかなり見づらくなるため、--outputの指定をtableにするとよさそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3024215/69dfe50e-40bc-c977-e90c-dc2605fb9252.png)


# 注意事項
StackSetsのデプロイが失敗しているかどうかの情報は本APIで取得することはできないためコンソールなどから確認が必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3024215/e508f0b5-4179-933e-c346-d433b472fa67.png)


# 参考URL
[自動デプロイ設定を把握できる新しい StackSets API](https://aws.amazon.com/jp/about-aws/whats-new/2024/03/visibility-auto-deployment-configuration-stacksets-api/)
[AWS CLI Command Reference - aws cloudformation list-stack-set-auto-deployment-targets](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudformation/list-stack-set-auto-deployment-targets.html)
「世界一流エンジニアの思考法」に学ぶ 効率的な問題解決へのステップ

エンジニアは、複雑で難しい課題に毎日直面します。そういった課題に対して、いかに適切に向き合い、効率良く解決できるかが肝心です。本記事では、世界一流のエンジニアからヒントを得て、生産的な問題解決のための5ステップをご紹介します。

### ステップ1: バグを見つけたら手を動かさずに仮説から考える
一流のエンジニアは、バグの発見時に手を動かす前に立ち止まり、根本原因を考えます。つまり、バグの症状から原因を推測し、仮説を立ててから対処します。単にデバッガでステップ実行するだけでは、表面的な対処にしかならず、根本原因が見えにくくなるためです。

まずは納得のいく仮説が立てられるまで、コードを読み込み、デザインドキュメントを確認し、テストデータで検証を重ねます。その過程で、バグの発生条件や影響範囲が明確になり、適切な修正方法が見えてくるはずです。

```mermaid
graph TD
  A[バグの発見] --> B[仮説構築]
  B --> C[検証とブラッシュアップ]
  C --> D[根本原因の特定]
  D --> E[適切な修正]
```
このように、一流のエンジニアは手を動かす前に頭を動かし、論理的に問題の本質を解き明かそうとします。この作業を怠ると、的を射た解決が難しくなります。

### ステップ2: メンタルモデルを活用して理解を深める

物事を深く理解するためには時間がかかります。一流のエンジニアは、メンタルモデル(頭の中の概念モデル)を構築することで、理解を加速させています。例えば、新しいプログラミング言語を学ぶ際には、その言語のパラダイムや設計思想を理解するメンタルモデルを作ります。こうすれば個別の構文を覚えるよりも、全体の構造が理解しやすくなります。

```mermaid
graph TD
  A[新しい概念] --> B[メンタルモデルに落とし込む]
  B --> C[全体構造の理解]
  C --> D[個別の理解]
```

### ステップ3: 理解を怠らず、根本原因を探る

表面的な理解に満足せず、「本当に理解できたか?」と問い続けることが大切です。一流のエンジニアは根本原因を徹底的に追及し、本質的な理解を目指します。例えば、アプリケーションの応答が遅い問題に遭遇した場合、単に処理を高速化するのではなく、パフォーマンス degradationの根本原因を特定することから着手します。理解が浅い状態で先に進むと、後々大きな障害につながりかねません。

```mermaid
graph TD
  A[問題の発生] --> B[表面上の対処]
  B --> C[問題が一時的に解決]
  C --> D[根本原因が残る]
  A --> E[根本原因の特定]
  E --> F[本質的な解決]
```

### ステップ4: 不要な作業を減らし、本質に集中する

多くのエンジニアは「できることを全てやろう」と考えがちですが、一流のエンジニアはそうではありません。彼らは本質的な価値がないと判断した作業は削ぎ落とし、重要な部分に注力することで工数を最小化しています。ソフトウェア開発の例でいえば、過剰な機能実装より最低限の要件を満たすことを優先し、リリース後に継続的に改善を重ねていく方が賢明です。  

```mermaid
pie
  "本質的な作業" : 30
  "不要な作業" : 70
```

### ステップ5: 最小の労力で最大の効果を

課題解決においても、少ない労力で大きな効果を生み出すことが理想です。一流のエンジニアは、80%の効果が20%の労力で得られることを心がけています。つまり、完璧を目指すよりも、手堅く価値の高い部分を押さえることを優先するのです。例えば、新しいシステムを構築する際、最初からスケーラブルで堅牢なアーキテクチャを作り上げるよりも、まずはMVPを早期にリリースし、負荷に合わせて順次改善していく方が現実的です。

```mermaid
pie
  "有効な労力 (20%)" : 20
  "過剰な労力 (80%)" : 80
```

### ステップ6: 失敗から学び、フィードバックを活かす  

一流のエンジニアは、失敗やフィードバックを歓迎します。それらは貴重な学習の機会だと捉えています。リリース後の不具合や顧客からの指摘など、自分の過ちを認め、フィードバックを実践に活かすことで、着実にスキルとプロセスを磨いていきます。アジャイルな開発手法がうまく活用できているかどうかも、自身のプロジェクト運営を常に見直すきっかけとなります。

```mermaid
graph TD
  A[プロジェクト実行] --> B[失敗/フィードバック]
  B --> C[改善の機会]
  C --> D[スキル向上]
  D --> A
```

### ステップ7: 失敗を歓迎するムードを作り、失敗を決して批判しないこと
一流のエンジニアが継続的な改善を行えるのは、チームの中で失敗を許容する風土があるからです。失敗は避けられないものと考え、それを恐れるのではなく、学習の機会と捉えています。失敗した際に非難されることがないよう、上司や同僚から失敗を批判されることのないムードづくりが重要となります。

```mermaid
graph TD
  A[失敗を恐れない文化] --> B[失敗から学ぶ]
  B --> C[改善が促進される]
  C --> D[生産性が向上]
```

以上の7ステップは、一見シンプルに見えますが、その実践は容易ではありません。しかし、日々の積み重ねの中でこれらのマインドセットを身に付けることで、より問題解決が可能となるはずです。一流のエンジニアに習い、プロフェッショナルとしての生産性を高めていきましょう。
loading画面の設定が原因でテストが失敗する

# 初めに
Jestとreact-testing-libraryを用いたプロジェクトのテストで、loading画面を表示する設定が原因でテストをPASSすることができませんでした。
このloadingはsupabaseからデータを取得しているのですが、そのデータの取得が完了するまで表示するものです。
もし同じことでつまづいている方の助けになればと思って共有します！

# 問題点
こちらがテスト実行するファイルです。
```react
/**
 * @jest-environment jsdom
 */

import { App } from "../App";
import React from "react";
import '@testing-library/jest-dom'
import { render, screen } from "@testing-library/react";

describe("Title Test", () => {
  it("タイトルがHello Jestであること", () => {
    // testId(title)を指定して取得
    render(<App />);
    const title = screen.getByTestId("title");
    expect(title).toHaveTextContent("Hello Jest");
  });
});
```

通常であればこの記述で問題ないと思います。
しかし今回はApp.jsxファイル内にloadingを表示するための記述をしていました。
```react
 .
 .
 .
const [loading, setLoading] = useState(true);
 .
 .
 .
useEffect(() => {
  const getTodos = async () => {
    try {
      const todos = await GetAllTodos(); //supabaseからデータを取得する関数
      setTodos(todos);
    } catch (error) {
      console.error("データの取得に失敗しました", error);
    } finally {
      setLoading(false); // useStateを用いてロード完了後、ローディング状態をfalseに設定
    }
  };

  getTodos();
}, []);
 .
 .
 .
if (loading) {
    return <div className="loading">ロード中...</div>; // ローディング中の表示
  }
  
return (
    <>
    <title data-testid="title">Hello Jest</title>
        .
        .
        .
```

この記述があるおかげ？でGetAlltodos()でsupabaseからデータを取得する前にテストを実行していました。
起こっていることとしては`const [loading, setLoading] = useState(true);`でデフォルトのローディング状態はtrueに設定されています。
この時点ではテストを実行しているので、ローディングはtrueのままテストを実行しています。
そして`if (loading) { return <div className="loading">ロード中...</div>; }`この記述でreturnを返しているため、処理はここで終了します。
つまりその下に記述されている`<title data-testid="title">Hello Jest</title>`はテストの対象となっていないということです。
そのため「Hello Jest」という文章を含んだタグはテストでは検出されず、エラーが返っています。

# 解決策
これを解決するためにはsupabaseからのデータを取得した後にテストを実行する必要があります。
そのようにするためにテストを実行するファイルを少し書き換えました。
```react
/**
 * @jest-environment jsdom
 */

import { App } from "../App";
import React from "react";
import '@testing-library/jest-dom'
import { render, screen } from "@testing-library/react";

describe("Title Test", () => {
  it("タイトルがHello Jestであること", async () => {
  // testId(title)を指定して取得
    render(<App />);
    const title = await screen.findByTestId("title");
    expect(title).toHaveTextContent("Hello Jest");
  });
});
```

この記述によりsupabaseからデータの取得が完了した後にテストを実行することができました（正確には**awaitはデフォルトでは1秒間の間、id=titleの要素がないかを繰り返し検索**するそうです）。

# 終わりに
今回の躓きがきっかけでテストの実行においてasync/await関数が原因で、テストが通らないこともあると発見できました。
それ以外にもファイル内でCSSファイルを読み込んでいた場合には、以下の記述がjest.config.jsファイル内にないと成功しないことも理解しました。
```react
moduleNameMapper: {
    "\\.(css|less)$": "identity-obj-proxy",
  },
```
今回のことを通じて「失敗は成功のもと」という言葉をより一層実感することとなりました。
Arduino CloudでガーデニングIoT -ベンチマーク-

## 背景
灌水を自動化するものとして、タイマーをセットして自動で水やりをしてくれる機器や雨センサーと連動して水やりの頻度を調節する機器などが市販されているが、ガーデニングIoTとしてはまだまだ発展途上である。



## 市販品

①雨センサー付き散水タイマー（タカギ）

蛇口から直接水を供給するタイプで、一戸建ての庭での使用を想定されている。灌水する時刻（0〜23時で1時間刻み）、灌水間隔（1日に2回〜7日に一回）、灌水時間（1〜10分1分刻み、10〜30分5分刻み、30〜90分10分刻み）を設定する方式。オプションの雨センサー（コード5m）を繋ぐことで、雨の日は灌水をキャンセルすることができる。タカギからはこの他にも自動で灌水をするための製品が発売されている。

https://www.amazon.co.jp/dp/B07BSX5Z7V/ref=sspa_dk_detail_2?psc=1&pd_rd_i=B07BSX5Z7V&pd_rd_w=5ZKBa&content-id=amzn1.sym.f293be60-50b7-49bc-95e8-931faf86ed1e&pf_rd_p=f293be60-50b7-49bc-95e8-931faf86ed1e&pf_rd_r=SXYKPSQMCWEVCBTW2R90&pd_rd_wg=Kg4Hu&pd_rd_r=7605a764-1e5e-4784-b9e5-056db9882389&s=diy&sp_csd=d2lkZ2V0TmFtZT1zcF9kZXRhaWw


②鉢植え用自動散水ドリップ（YITIKA）

タンクからポンプで水を汲み上げ、OUTに繋がったホースを分岐させて各ポットに灌水する。充電とタンクへの給水を定期的に行うことで、マンションのベランダでも使用可能。頻度（0.5日に一回〜15日一回）と灌水時間（1〜10分）を手動で設定する


https://www.amazon.co.jp/鉢植え用自動散水ドリップ-システム10鉢対応可能-セット10mホース付き-防水LEDディスプレイと大容量バッテリー、正確な配水-ガーデン花植木鉢自動給水器/dp/B0881M1V22/ref=sr_1_7?__mk_ja_JP=カタカナ&crid=3FM0NSEILJI4D&dib=eyJ2IjoiMSJ9.tSly7n1vfJ69hQMbdtzNO1Bo6ReUlOpkvPpIC0VAIBBS_F3AiwyknA65AcGvt1r9l4_SBC6Pml6Ejz3QuprEJ_HaFR44XMld2KWt8jGvGoTkV39Z2vc2YE33xTNepC-ezEBBB9G1QrFn1BOxboLkGlcBHMdzIFS5xaGVtBKPG0EAtHquPsw_-lWZHUsAmR5zeBdChKv33h4YyIDbep0ROyVAqUSV5bF5fAeH91La_OUXVqMxcG5RD4ijXSm9w1iFuIk5jSaWqPuESgbubAiH_wTjxngCYLvHP17a98x789k.-YBelCtmb-15PTLKO3poi8xKq_2q1m910e5Fjt937LA&dib_tag=se&keywords=自動水やり&qid=1711790055&sprefix=自動水やり%2Caps%2C265&sr=8-7&th=1

③スマートプランターIvy

様々なセンサーとAIによって、環境と植物の状態を表情として可視化するスマートプランター。
調べた限りではガーデニング関連製品で最もスマートな製品で、36種類のデータベースから植物の種類に合わせた反応をする。スマートホームとも繋がるそう。

https://www.amazon.co.jp/Ivyスマートプランター-スマートケア＆スマートペット-スマートホーム機能搭載-インテリアグリーン-日本語取扱説明書【植物付属ありません】/dp/B0CDPL6D76/ref=asc_df_B0CDPL6D76/?tag=jpgo-22&linkCode=df0&hvadid=676128400529&hvpos=&hvnetw=g&hvrand=9098205634218833014&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009552&hvtargid=pla-2245685324119&mcid=90db8262e0a53cf69fe6cc3178c74782&gad_source=4&th=1

## 目標

その他にも類似品は市販されているが、そこで、Arduinoを活用して市販品よりもスマートなガーデニングIoTの構築を試みる。センサーを用いて土壌や植物、季節に応じてコントロールする以下の機能を実装したい。

- 灌水管理
- 温度管理
- 二酸化炭素濃度管理
- 日照管理
- 肥料管理

また、市販品は一つのプランターで完結した製品ばかりで、複数のプランターが有機的に繋がるようなシステムを目指す。まずは、最も重要な灌水管理（水やり管理）を実装していく。
[Python, chatGPT] 非同期処理がよく分からなかったのでchatGPTに聞いてみた

# はじめに
筆者は去年4月にIT系の企業に入社したばかりの、新米ITエンジニアだ。
情報系のバックグラウンドもなく、大学院にいた頃の研究分野は全く関係ない分野で、プログラミングも趣味や研究で軽く触れた程度だ(よく入社できたな)。
何が言いたいかというと、この記事はプログラミング初心者が書いた記事なので正確性等は保証できないという話だ。

## 記事執筆の経緯
業務や社内の勉強会など色んな場面で「非同期処理」という言葉を聞くようになった。研修でもやった気がするが、その場では理解できず、復習する時間も取れなくてそのまま放置になっていた。
流石に理解しとかないとマズそうだなと思い、本記事の執筆に至る

## chatGPTに聞いてみた
というわけで本題。「非同期処理」を理解しよう！
ただ、今までも非同期処理そのものをググったりchatGPTに聞いたりしたことはあったのだが、いまいちパッと理解ができなかった記憶がある。
そこで、「プログラマたるもの手を動かしてなんぼっしょ」ということでchatGPTに非同期処理の振る舞いを実感できるコードを書かせて、それを実際に動かしてみた。

### 投げたプロンプト
> 非同期処理について理解したい。
簡単なコードで、非同期処理の振る舞いを実感できるような題材をくれ

### chatGPTさんからのお返事
> 非同期処理を理解するための一般的な例は、Webからのデータのフェッチや時間がかかる処理のシミュレーションです。Pythonでの非同期処理を示すシンプルな例を紹介するよ。ここでは、asyncioモジュールを使用して非同期処理を行う。以下の例では、非同期に複数のタスクを実行し、それぞれが完了するのを待つ方法を示す。
このコードでは、count関数が非同期に実行される。asyncio.sleep(1)は非ブロッキング待機を示し、この間に他のタスクが実行される余地を提供する。main関数ではasyncio.gatherを使って、3つのcountタスクを並行して実行する。結果として、プログラムの全体的な実行時間は大幅に短縮される。

> この例から、非同期処理が複数のタスクを効率的に並行して処理できることを実感できるはずだ。非同期処理は、待機時間が長いタスク（例えば、ネットワークリクエストやデータベースへのクエリ）に特に有効である。

### お出しされたコード
```python async.py
import asyncio

async def count():
    print("One")
    await asyncio.sleep(1)  # 非同期に1秒待機
    print("Two")

async def main():
    await asyncio.gather(
        count(),
        count(),
        count()
    )

if __name__ == "__main__":
    import time
    start = time.time()
    asyncio.run(main())
    end = time.time()
    print(f"Finished in {end - start} seconds.")
```

### 実行結果
```bash
$  python async.py 
One
One
One
Two
Two
Two
Finished in 1.0014240741729736 seconds.
```

### 解説・雑感
いかがだっただろうか。
見てわかる通り、`count` という1秒かかる(かける)処理を用意してあげて、それを3つ同時に実行したらどうなるか？ということをしている。
要するに、「1秒かかる処理が3つあっても並行してやれば1秒で終わるよね！」という話だと思われる。
もっと話を分かりやすくするために、非同期「じゃない」処理 (=同期処理と言って良いのか？)をchatGPTに実装してもらった。

### chatGPTに実装してもらった非同期「じゃない」処理

```python non_async.py
import time

def count():
    print("One")
    time.sleep(1)  # 同期的に1秒待機
    print("Two")

def main():
    count()
    count()
    count()

if __name__ == "__main__":
    start = time.time()
    main()
    end = time.time()
    print(f"Finished in {end - start} seconds.")
```

### 実行結果

```bash
$ python non_async.py
One
Two
One
Two
One
Two
Finished in 3.015064001083374 seconds.
```

### 解説・雑感
こちらは3つの処理が直列しており、1つの処理が終わってから次の処理を実行するという流れになっている。
実行結果を見ればわかる通り、非同期処理の場合 Oneが3つ並んでからTwoが3つ並んでいる (= 1つ目の処理が終わる前に2つ目の処理が終わっている) のに対し、同期処理の場合 "One Two" が3つ並んでいる (= 1つ目の処理が終わるまで待ってから2つ目の処理が始まっている)
そして、かかる時間も同期処理の場合は約3秒なのに対し、同期処理の場合は1秒で済んでいる。
この「時短できる」という性質が、「非同期処理」なんてものが(おそらく色々なプログラミング言語で)わざわざ実装されている理由なのだろう。

## なんとなく分かったけど
今回の記事の執筆を通して、非同期処理についての理解が深まった。執筆前は非同期処理について聞かれてもほぼ説明できないレベルだったが、今は最低限の説明はできそうだ。
ただこれでは不十分。
まず今自分の頭の中では「並列処理みたいなもの」という大雑把な理解に留まっているが、わざわざ「非同期処理」という言葉を使っているあたりイコールで結びつけられるものではないのだろう。今後ちゃんとした定義について勉強する必要がありそう。
そして何より大事なのが、「非同期処理を適切に実装できること」。非同期処理によってプログラム全体の実行時間を短縮できる場面で、非同期処理という機能のことを思い出してちゃんと実装できる必要がある。
上記の2点は、今後の宿題といった感じだろうか。

# 終わりに
見て分かる通り、勉強中の内容をあえて途中でアウトプットしている。なので内容も理解も不十分だったり不正確だったりするかもしれないことは、改めて強調しておく。(誤っていたらご指摘くれると嬉しいです……！)
改めて、(ほぼchatGPT頼りとはいえ)実際にコードを動かして「具体例」を見ると、プログラミングにおける概念の理解は深まるものだと実感。そして、chatGPTはそういったコードの具体例の出力に長けているということにも気づいた。
今後も、chatGPTを活用したりしてプログラミングの様々な知識や概念を学んでいきたいと思う。
そんなプログラミング初心者の決意表明じみたぼやきなのであった。完。
Google PhotosからAmazon Photosに移行

# はじめに

Google Photosの利用量が増加してしまって、無料の15GBに近づいてしまっているので、Amazon Primeで無制限に利用できるAmazon Photosに移行します。

# Google Photoのエクスポート

Google Photosの全てのPhotoを保管するのに、Google PhotosアプリだったりPCのブラウザから画像を選択してダウンロードするのだと気が遠くなりそうでしたが、”Google データ エクスポート”というのを見つけられました。

https://support.google.com/accounts/answer/3024190?hl=ja


あとで探してみるとブラウザでアクセスした Google Photoの設定（画像右上の歯車）でデータのエクスポートという部分からも同じことができるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/fb036fbb-3a5d-b20e-f97c-7c87baace0a3.png)

ここでは2022までをエクスポートして、それ以降のものはまた次の時にとしておきました。


# エクスポートしたものをダウンロード

エクスポートが完了するとダウンロード用のリンクがメールで届くので、すべてダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/847f9682-f0ae-5022-9539-91015bf75c4b.png)

unzip してディレクトリに解凍します。

# 重複ファイルの削除

PCからアップロードしていたファイルなどと重複している可能性があるので、ファイルを削除します。DupeFileEliminatorというのでディレクトリを指定して、検索をさせて削除させました。エクスポートしたものを残すようにしたかったので、作成日時があたらしいものを ”削除対象" "自動" で残すようにして削除させました。

https://forest.watch.impress.co.jp/library/software/dupfileeli/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/4018465e-8d10-7e7a-ff13-934ea96aeeef.png)

削除に多少の躊躇はありますが、重複させていても意味がないので一気にいってしまいます。

# Amazon Photos にアップロード

Windows版 Amazon Photos アプリをダウンロードして、同期をします。

https://www.amazon.co.jp/b?ie=UTF8&node=5262649051

インストールして、起動、ログオンをすると何をアップロードするか選択します。ビデオは5GBまでなので、ビデオはローカルだけになってることを認識しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/f65f921b-1231-4967-fe69-9488c42a1727.png)


バックアップ ＞ バックアップの追加 で Google Photosの解凍先ディレクトリやPCのディレクトリが含まれるようにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/9d608753-6fd2-dc7b-ac55-7ae00beb3075.png)


# Google Photosの削除

Google Photosで写真を選択（Shift押しながらだと複数選択できる）して削除します。まとめて削除の方法はみつけられませんでした。


# まとめ

Google PhotosからAmazon Photosに移行 します。Amazon Photos使ってみて Google Photosのほうが使いやすければ課金もありかもしれません。
インスタンスタイプを変更したらネットワーク設定が初期化された件

# はじめに
弊社で運用しているWindowsインスタンスでネットワーク設定が初期化される事象が発生しました。

# 前提
・OSはWindows。
・IPアドレスはDHCP。
・ドメイン参加のため、DNSサーバは固定で、外部のNetwork内にあるActiveDirectoryサーバを設定しています。
・事象の発生直前に、ネットワーク設定関連の作業は行っていない
・直近の変更はインスタンスタイプの変更のみ

# 対応
インスタンスタイプの変更により、ネットワーク設定が変更されていることがわかりました。
インスタンスタイプを元に戻すと、ネットワーク設定も元に戻ることを確認できました。
事象はM4系からM5系などの世代変更で発生しますが、サイズの変更だと問題ありません。
弊社では作業前後で設定を比較し、スクリプトで再設定することで再発を防止しています。

# 原因
調査用に新たにインスタンスを作成し確認を行いました
EC2では、インスタンスの世代が変わると機器構成も変わるようです。
CPUが変わるため、チップセットなどの構成も変わるのはやむを得ない状況でしょう。

しかし、ネットワークに関しては、Windowsではデバイス単位で設定を管理します。
ipconfig /allコマンドで確認すると、以下のようになります。

m4.large（IP固定）の場合
![スクリーンショット 2024-03-31 173711.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669193/c911020e-1a77-dbcc-e663-83fbf2f669ce.png)

m5.large（IPはDHCP取得）の場合

![スクリーンショット 2024-03-31 174732.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669193/a54e8765-e131-7e89-5c71-98d71af4b04e.png)

説明にあるデバイスが異なるため、OS上では別の扱いになります。
Windowsのデバイス一覧内でも、以下の表示になります。
非表示デバイスを有効にすることで、過去に使用したデバイスも一覧に残っています。

![スクリーンショット 2024-03-31 175048.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669193/5cd297fb-ba56-9fa3-ab9b-9f30b6b9d86c.png)

m4.largeを使用した場合に設定した場合は、Intel® 82599 Virtual Functionの設定が利用されますが、
m5.largeを使用した場合に設定した場合は、Amazon Elastic Network Adapterの設定が利用されます。
m4.largeからm5.largeへの変更では、Amazon Elastic Network Adapterの設定が使用されるため、設定がされていない場合は初期状態となります。

設定はレジストリ上でも確認可能です。
格納場所は以下です。
該当デバイスのフォルダを削除した場合は初期設定となります。

IPアドレスとDNSサーバの固定設定であれば、DHCP オプションセットの利用が簡単で良いと思います。

レジストリの設定は、インターフェース（ネットワークアダプタ）の設定です。
場所は以下の通りです。
\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\

![スクリーンショット 2024-03-31 204028.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669193/57edc045-6800-7c93-565f-7cbbdba69e7d.png)



IPアドレスの設定は、以下に保存されます。
\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\
に保存されます

以下はm4.large（IP固定）の設定
![スクリーンショット 2024-03-31 215208.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669193/60cc1216-a3bd-e93e-1037-da84a23a3d10.png)

以下はm5.large（IPはDHCP取得）の設定
![スクリーンショット 2024-03-31 215238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669193/ebe372c9-0e33-72e6-e197-725b21b71dcc.png)

# 注意事項
## 
インターフェース（ネットワークアダプタ）の設定を確認すれば分かると思いますが、
ipconfigで表示される項目以外にも、MTUなどの設定もデバイスに基づいた設定となります。
ネットワーク設定の調整を行ったインスタンスに対して、インスタンスの世代を変更する場合は、引き継がれない設定があることに注意してください。

##
IPを固定する場合は、デバイス単位でのIP設定を行うため、OS上の複数のデバイスで同じIPを使用する事になり、以下のメッセージが表示されますが、問題ありません。

![スクリーンショット 2024-03-31 215643.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669193/8a8cf1df-5c3d-7f6a-a180-1252424510e7.png)
プロジェクトマネジメントを初めて経験して学んだこと

## はじめに
3月末まで初PMとしてプロジェクトを管理してきました。
- PG: 5名
- ウォーターフォール開発
- 開発期間: 6ヶ月（詳細設計〜結合テスト）

その中でたくさんの失敗を繰り返してきました。
今後PMを任された際に同じ過ちを繰り返さないために、
今回学んだプロジェクトを管理する上で重要と感じたことをまとめます。

## 1. 作業のみえる化を行う
作業の見える化は、プロジェクト管理で最も大切なことです。
各メンバーの作業が明確になり、ゴールと現実とのギャップが明確にできます。
また、作業遅延が発生した際もすぐに把握する事ができ対処が可能になります。
作業の見える化と見える化を維持する運用ルールの徹底が重要です。

見える化を実現するために、WBSや課題管理表などのツールを用いて管理される事が多いです。
ツール選定時に重要なことは、ツールのメンテナンス性になります。
ツールが扱いづらいものであると、作業状態が把握できない、最悪管理を怠り機能しなくなることもありえます。
必要十分な情報を管理できるか、管理の手間はないかはツール選定時に重要なポイントです。

## 2. 課題は事前に潰す
後戻りを防ぐためには、課題が発生する前に事前に潰すことが大切です。例えば、
- プロジェクト計画時に各フェーズのアウトプットを明確にする
- 要件定義時に限界値、性能目標、UIイメージを詳細に詰める
- 設計時に実装方針を検討する
- 開発フェーズにテスト方針を検討する
- コーディング前にレビュー観点を明確にする
などなど…

## 3. 突然湧いてきた課題と向き合う
事前に潰せる課題を潰したとしても、課題は常に発生します。
誰がいつまでに対応するか。対応するためのボトルネックは何か。そのボトルネックはいつまでに解決できるか。
これらの観点で発生した課題とは常に向き合い、管理（ツール）への反映を行う事が大切です。

## 4. それは誰のタスク？
そのタスクは誰がやるべきか、いつまでにやるべきかを明確にすることも大切です。
ポイントは自分で抱え込まないこと。まず他のメンバーで回せないかを考えること。
PMの仕事はプロジェクト管理と社内プロセス管理で一杯一杯になってしまいがちです。そこで自分に追加タスクを振ると
自身がボトルネックになってしまい結局プロジェクトメンバーに迷惑をかける結果になってしまいます。
時間が空くなら次フェーズの検討をするくらいがちょうど良いくらいです。

## 終わりに
次回のプロジェクトを回す際は一度この内容を見返して同じ過ちを繰り返さないようにしたいと思います。
最後まで読んでいただきありがとうございました。
Roadmap.sh学習メモー認証（１）

# このドキュメントについて
- Roadmap.shのバックエンドデベロッパー学習メモです
- 学ぶ前の認証についての認識：
	- 認証ってなんだっけ？認可と混同されがちということは聞いたことがあるけど、どっちがどっちなんだっけ？
	- 認証関連の用語・技術としてBasic認証とかシングルサインオンとかいろいろあるよなぁ。いまいち整理して理解してないかも
# 認証と認可
## 認証
- Webシステムやコンピュータに対してユーザが「ユーザ本人」であることを証明すること
- 例：AさんがWebサービスにログインするとき自分のアカウントID、PWを入力する。PWはAさんとWebサービスだけが知っているので正しいパスワードでログインできるのはAさんだけである

## 認可
- 「認証」とよく似た言葉に「認可」というものがある
- 「認可」は「認証」されたユーザがとることのできる操作を許可すること
- 例：アカウントID：userAに対して管理画面の操作権限を与えること


つまり
「認証」＝だれであるか　の証明
「認可」＝何ができるか　の許可

# 認証方式
## Basic認証
ID、PWによる認証のこと**ではない**。
正確にいうと、Basic認証でもIDとPWは用いるが、「ID、PWによる認証」すべてがBasic認証というわけではない。
Basic認証はHTTPの仕様の一部で、[RFC 7617](https://tex2e.github.io/rfc-translater/html/rfc7617.html)で定義されている。

Basic認証ではユーザID、PWをBase64でエンコードしたものをHTTPヘッダのAuthorizationフィールドに記載しサーバーへ送信する

また、ユーザがブラウザを閉じるまで認証情報が有効なので明示的なログアウトができない。

## Digest認証
Basic認証の改良版。こちらは[RFC 7616](https://tex2e.github.io/rfc-translater/html/rfc7616.html)で定義されている。
以下の手順で認証情報をハッシュ化することでBasic認証よりもセキュアに認証情報を送信する。

1. サーバ：ランダムな文字列を生成し、ID、PWの入力を求める
2. クライアント：PWに1で生成されたランダムな文字列を付与し、ID、PWをハッシュ化、サーバへ送信する
3. サーバ：クライアントから送信された認証情報とサーバ側で管理しているID、PWおよび1で生成した文字列のハッシュが一致するか検証する


HTTPSによる通信が普及している現在ではベーシック認証でも平文で認証情報を送るリスクは軽減できるので
ダイジェスト認証の重要性は低下している。らしい。

## Form認証
HTMLでつくられたフォーム画面からID、PWを入力してに認証する方式。
ブラウザからの入力は通常、平文で送信されるのでHTTPSを用いて通信するのが一般的。
HTTPの仕様にない認証方式のため開発元が自身で実装する必要がある。

逆に言うと独自で実装できるので外部サービスを使った認証や2段階認証、SSO（シングルサインオン）などPW以外の認証方式と組み合わせられる。
Basic認証やDigest認証と異なり、セッション管理をすることで「ログアウト」機能を実装することもできる。

### セッション管理
セッションとはログイン～ログアウトまでの一連の操作や通信のこと。
HTTPはステートレスなので**複数のリクエストがあったときに、それが一連の操作や通信である**ということをサーバ側で判断するには工夫が必要。
（そもそもログイン後にページ遷移したときに、それがさっきログインしたユーザであることをサーバ側でどのように判別したらいいだろうか？ログイン後の通信ではIDやPWは送らないのに）

その工夫が**セッション管理**。

#### Cookieによるセッション管理
Cookieによるセッション管理について説明する。
まず、ユーザがWebアプリケーションにログインするとサーバ側では一意なID（セッションID）を発行する。
そしてIDをブラウザのCookieに保存する。
以降、クライアントはこのIDを含むCookieをサーバに送ることで
サーバ側で複数のリクエストが「**一連の操作**」であることを認識する。

ECサイトを例にとるとセッション管理は以下のような流れになる。
1. クライアント：AさんがECサイトにログイン　⇒　サーバ：セッションID（XXX）を発行、Cookieに保存する
2. クライアント：Aさんが商品をカートに入れる　⇒　サーバ：セッションID＝XXX　のユーザのカートに商品を保存する
3. クライアント：Aさんが商品を購入する　⇒　サーバ：セッションID＝XXX　のユーザのカート内の商品の購入処理をする
4. クライアント：ＡさんがECサイトからログアウトする　⇒　サーバ：セッションID（XXX）の情報を破棄する

セッション管理の方法にはCookie以外にURLやフォームの隠し要素にセッションIDを含める方法もあるが
Cookieで管理する方法が主流

また、通常セッションIDには有効期限を設けておく、これによりセッションIDが他者に知られても
一定時間経過後にはログアウトさせられる。
## 参考
- [Basic認証 Digest認証 Form認証の違い - ITを分かりやすく解説 (medium-company.com)](https://medium-company.com/http%e8%aa%8d%e8%a8%bc%e6%96%b9%e6%b3%95%e3%81%ae%e7%a8%ae%e9%a1%9e%e3%81%a8%e9%81%95%e3%81%84/#google_vignette)

- [Basic認証とは？仕組みやメリット・デメリット、設定方法について解説｜セキュリティのSHIFT (shiftinc.jp)](https://service.shiftinc.jp/column/9782/)

---
Roadmap.shにはダイジェスト認証とか出てこなかったけどいろいろ調べてたら芋づるで出てきた。
トークンベース認証やSSO、多要素認証とかは後日。
リストに指定したファイルをコピーしてくる

# 経緯
いろんなパスからファイル取得する割に整理されていなかったりするので欲しいファイルを記述して取ってくるバッチ作成
残して置いていつでも使えるように記事にした。


```FileCopy.bat
@echo off
setlocal enabledelayedexpansion

rem リストファイルのパス
set "listFilePath=C:\Work\list_file.txt"

rem ネットワークドライブのパス
set "drivePath=\\Fileserver\log"

rem 日付のフォーマット
set "dateFormat="

rem 一時ディレクトリにコピーする先のパス
set "destinationPath=C:\Work"

rem リストファイルからファイル名を取得してファイルをコピー
for /f %%i in (%listFilePath%) do (
    rem ファイルのフルパスを構築
    set "sourceFilePath=!drivePath!\%%i!dateFormat!.*"
    
    rem ファイルをコピー
    copy "!sourceFilePath!" "%destinationPath%" /Y
)

endlocal
pause
```

<br>
コメント多めに書き残したので特別に説明とか書いておかなくても困らないはず
【生成AIアプリ100チャレンジ】（8）アイコン生成アプリ

https://ai.nuevo.jp/icon/
ユーザーのみ

# 開発環境

|              |              |
|:------------:|:------------:|
| Server       | lightSail    |
| Language     | Python3.11   |
| Framework    | Django       |
| DB           | sqlite3      |

ローカル環境ではPythonのvenvを使用。エディタはvs codeです。


# 目的
トップページのアイコンがあまりに統一感がないので、統一感が出るようにプロンプトを作成した。
日本語よりも英語の方が精度がアップするだろうと考え、イメージのみDeeplで英語に翻訳している。
フォームはセレクトボックスにして、固定。
プロンプトにもプレースホルダーを設置し、変数を外から挿入できるようにした。

# コード
```python:qiita.rb
@login_required
def index(request):
    OPENAI_API_KEY = os.environ['OPENAI_API_IMAGE_KEY']
    API_KEY = os.environ['DEEPL_API_KEY']
    translator = ""
    image_url = ""
    if request.method == "POST":
        form = ChatForm(request.POST)
        if form.is_valid():
            color = form.cleaned_data['color']
            background = form.cleaned_data['background']
            image = form.cleaned_data['image']
            def translate_text(text, auth_key):
                translator = deepl.Translator(auth_key)
                result = translator.translate_text(text, target_lang="JA")
                return result.text
            image = translate_text(image, API_KEY)
            client = OpenAI(
                api_key = OPENAI_API_KEY,
            )
            response =  client.images.generate(
                model   = "dall-e-3",
                prompt = f"""Design a pictogram-style app icon, using only two HEX color codes: ‘{color}’ for the icon itself and ‘{background}’ for the background. The theme is based around {image}, and the icon should feature a single, central symbolic element related to {image}. The design must strictly adhere to using only these two specified colors, creating a clear and simple two-tone image that effectively communicates the theme.""",
                size="1024x1024"
            )
            image_url = response.data[0].url
    else:
        form = ChatForm()

```

比較的統一感のあるアイコンを作成できるようになった。バラバすぎて少しキモい漢字だったトップ画面のアイコンが整理された気がする。

# TOP画面
![スクリーンショット 2024-03-31 22.47.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/888267e0-c415-2430-7c38-1f16518833c9.png)


# アプリ画面
![スクリーンショット 2024-03-31 22.49.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/d6637799-b296-38c6-6567-7ba25c978b84.png)

# 感想
　統一感がなかったアイコンたちの並びが綺麗になった。
　日本語のプロンプトをサーバ側で英語に翻訳して精度をあげること。テンプレート側でプロンプトを制限させること。プロンプトにプレイスホルダーを使用すること、chatGPT4にプロンプトを考えさせることなど新しい試みもあった。
　今回は画像をたくさん作ったのでコストがかかって怖かった。画像生成はお金がかかるのでね、プロンプトの精度には気をつけたい。
WSL(Ubuntu 22.04 LTS) に、ＤockerとDocker Composeをインストール

# 環境
Windows10 + WSL(Ubuntu 22.04.3 LTS) 
※Windows11でも確認済み

```bash
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
...
```

# はじめに
最近何かと再インストールするタイミングが多いので、書き残し。

# 手順

## 事前に
1. 初回立ち上げ前にPowershell(管理者実行)で以下を行う
```shell
wsl --set-default-version 2
```

2．仮想環境単体にPowershell(管理者実行)で以下を行う
```shell
wsl --shutdown
wsl --set-version <WSL管理名称> 2
```
※WSL管理名称 は `wsl --list --verbose` とかで確認

3．WSLのバージョンを最新にするためコマンドプロンプト(or Powershell)で以下を行う
```shell
wsl --update
```
※最新はsystemctlが面倒な設定なしで使えます

## Dockerのインストール
```bash
# パッケージ更新
sudo apt -y update

# 必要なパッケージの追加
sudo apt install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# 証明書ダウンロード
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 証明書登録
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

# パッケージ更新
sudo apt -y update

# Dockerのインストール
sudo apt -y install docker-ce docker-ce-cli containerd.io

# dockerを自動起動設定
sudo systemctl enable docker

# 操作ユーザーをグループに追加
sudo gpasswd -a $USER docker

# 一旦ターミナルを閉じる(グループ追加反映のため)
exit
```

## Docker Compose のインストール （どちらかを実行すればOK）
- aptで直接
```bash
# docker-composeインストール
sudo apt install -y docker-compose
```

- Curlで取得したファイルベース
```bash
# フォルダ作成＆移動
sudo mkdir -p /usr/local/libexec/docker/cli-plugins
cd /usr/local/libexec/docker/cli-plugins

# https://github.com/docker/compose/releases にアクセスして、最新をバージョンを確認(設定値は記載時最新)
DOCKER_COMPOSE_VERSION=v2.26.1

# compose の実行ファイルを取得
sudo curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64 -o docker-compose

# 権限変更
sudo chmod +x docker-compose
```

# 余談(以前の話)
1. 自動起動
以前は、sudoersを編集したり、nsloginからsystemctl実行して…というのをしないといけなかったのが面倒でしたが、現在はsystemctlがデフォルトで実行でき、ラクになりました。
1. iptable
以前は、iptableのバージョンを落とさないといけないタイミングがありましたが、今は必要ないみたいです。
※イメージが取得できなかったりしたはず…

# 最後に
ここ１～２年で細かいところが変わっていましたが、やっと落ち着いてきたように感じます。

# 参考

https://rimane.net/wsl2-docker-setup/
PCでBluetooth使いたい

# やっぱりPCでBluetooth使いたい

## 動機

少しでも安くなると思って、Wifiが内蔵していないマザーボードを購入してはや数ヶ月。  
やっぱりPCでBluetoothイヤホンを使いたい。  
Bluetooth便利  

## 結論

下記のBluetoothアダプタを購入しました。  
理由としては、メーカー公式でサポートしている無線LAN化キット  
`AMD 802.11ax Wi-Fi 6E Kit`が入手困難だからです。

- メーカー
  - IO-DATA
- 製品
  - USB-BT50LE  
- 参考サイト
  - https://kakaku.com/item/K0001428458/?lid=20190108pricemenu_ranking_1_rating_pricedown

## お願い

ASRock B650 PG Lightningに無線LANカードを後付した経験のある方  
どんな製品を使ったか、ご享受いただけるとありがたいです  

## PCパーツ

- マザボ
  - ASRock B650 PG Lightning
- CPU
  - AMD(型番忘れ。。)

## 調査開始

### Bluetoothの後付方法を検索

PCにBluetoothを後付する場合、検索結果の上位に出るのはやはりドングルでした  
以前にTP-Linkの[UB400](https://www.tp-link.com/jp/home-networking/computer-accessory/ub400/)を使用していましたが、AirPods Proと接続をすると  
どうにもプツプツと切れる音がなっていました。  
これが嫌でドングルタイプは一旦候補から外しました。

### ASRock公式のkitがあるらしい

色々と調べる中で下記のサイトでASRock公式の後付できる  
無線LANカードが販売されていることを発見しました  

https://shop.tsukumo.co.jp/goods/4710483938146/

### Intelチップセットという文字が気になる。。

ページを見ているとIntelチップセットと言う単語が出てきていて  
それがものすごく気になりました。  

https://shop.tsukumo.co.jp/goods/4710483938146/

私の使用しているCPUはAMDの製品。。  
これは使えるのか？

### 公式サイトで対応マザボ一覧を確認

`Intel 802.11ax Wi-Fi 6E Kit`の公式サイトでサポート対象のマザボに  
使用しているマザボ`B650 PG Lightning`が存在するか確認しました。  

https://www.asrock.com/mb/spec/product.jp.asp?Model=Intel%20802.11ax%20Wi-Fi%206E%20Kit#Support

サポート対象に存在しない！？

### AMD用のkitを発見

調査を進めると`AMD 802.11ax Wi-Fi 6E Kit`なるものが存在することがわかりました。  
早速HPでサポート対象かを確認します。  

https://www.asrock.com.tw/mb/Spec/product.jp.asp?Model=AMD%20802.11ax%20Wi-Fi%206E%20Kit#SupportList

ありました、ビンゴです！

### AMD 802.11ax Wi-Fi 6E Kitを購入するサイトが見つからない。。

これで安心と、`AMD 802.11ax Wi-Fi 6E Kit`を購入できるサイトを探したのですが  
いつも利用しているサイト「アマゾン、ドスパラ、つくも等々」のどこにもありませんでした。  
海外のサイトでいくつか見つけましたが、流石に日本語のサポートも受けにくいと考え  
購入を見送りました。

### 最終的にはドングルに

結論でも記載した通り、ドングルを購入しました。  

以前使用していたUB400はBluetooth4.0対応だったので  
もしかしたらそれの影響かもしれないですし  
比較的高いドングルがどのようなものなのか試すには良い機会だと思うようにしています。  

## 所感：無線LANカード、よくわからん

今回の調査で、無線LANカードのインターフェースにはPCIe以外にもM.2がある  
チップセットにはIntelとAMDがある等、今まで知らなかったことを知ることができたので  
有意義な時間になりました。ただ、やっぱりまだ無線LANカード、よくわからんです。

中古の無線LANカードを入手できたら、古いPCに繋いで見たりして遊びたいなと思います。