OmniAICreator/Qiita-1.07M huggingface dataset:
git-flow、Github-flowとは

いままで開発を進めてくるにあたり、なんとなくgit-flowを使っていたので、まとめてみる。

# git-flow
デスクトップ／モバイルアプリケーションのように「リリース」を必要とするソフトウェアの開発に適している。

<img width="500" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601125/89ee1347-8e7d-495e-8eaf-99381a7fd640.png">
> 引用：　https://nvie.com/posts/a-successful-git-branching-model/

## ブランチの種類と用途
ブランチはメインブランチとサポートブランチに分類される。

メインブランチ（常に存在するブランチ）: `main`, `develop`
サポートブランチ（タスクに応じて作成する）: `feature`, `hotfix`, `release`

### mainブランチ
- リリース済みのコードを管理するためのブランチ


### developブランチ
- 開発中のコードを管理するためのブランチ

### featureブランチ
- developブランチから派生させる。
- 主にこのブランチで開発を進めていき、マージするときにはdevelopブランチへマージする。
- マージしたあとは基本的には削除していく。
- 命名は`feature/タスクの内容`または`feature-タスクの内容`とする。

### releaseブランチ
- developブランチから派生させる。
- リリース前の準備を行うブランチ。
- マージ先は、developブランチとmasterブランチ
- 命名は`release-タスク内容`

### hotfixブランチ
- mainブランチから派生させる。
- 緊急の修正対応などを行う。
- マージ先はdevelopブランチとmasterブランチ
- 命名は`hotfix-タスク内容`

個人開発では、releaseやhotfixブランチを使うことがなかった。

## Github-flow
git-flowよりもシンプル。1日に数回、デプロイを行うようなWebアプリケーションの開発に向いているらしい。
`master`ブランチと`feature`ブランチのみで開発を進めていく。

### Github-flowの6つのルール
1. masterブランチは常にデプロイ可能な状態
1. 作業用ブランチはmasterブランチから派生させる
1. 作業用ブランチは定期的にプッシュする
1. プルリクエストを活用する
1. プルリクエストが承諾されたら、masterにマージする
1. masterへのマージが完了したら、すぐにデプロイを行う

## まとめ
githubチームは`Github-flow`を使っているようで、そのシンプルな設計が確かに良いと感じた。良さを活かすためにはルールの`3. 作業用ブランチは定期的にプッシュする`と`4. プルリクエストを活用する`が結構大切だと思いながら参考資料を読んでいた。
開発する速度や種類によって使い分けが必要だと思うが、個人開発などでは`Github-flow`で開発していれば大丈夫だと思う。

## 参考
https://nvie.com/posts/a-successful-git-branching-model/

https://gist.github.com/Gab-km/3705015
C#でカノジョをつくる（8日目）

お疲れ様です。たなしょです。
今日で[スラスラわかるC#](https://www.amazon.co.jp/%E3%82%B9%E3%83%A9%E3%82%B9%E3%83%A9%E3%82%8F%E3%81%8B%E3%82%8BC-%E7%AC%AC2%E7%89%88-%E4%BA%94%E5%8D%81%E5%B5%90-%E7%A5%90%E8%B2%B4/dp/4798154636)も読み終わります。
非同期処理を勉強しました。今日も記事の内容は短いです。

## マルチスレッドプログラミング
複数の処理を並行して行うことをマルチスレッドプログラミングといいます。
C#では`非同期メソッド`と`lockステートメント`の2つの言語機能を持っている。

## 非同期メソッド
メインスレッド以外処理を実行することです。
JavaScriptと同じように`async`、`await`で記述されます。

### async
asynkキーワードは通常のメソッドと挙動が変わりません。awaitと呼ぶ際にペアで必要です。

### await
awaitキーワードは別スレッドに制御を移して、処理完了後に続きの処理を再開します。

## 排他制御
マルチスレッドプログラミングでは競合状態はよく起こるので、ほかのスレッドが同じ場所を実行しないように排除する処理のことを排他制御と呼びます。

C#の排他制御のための構文で`lockステートメント`を持っています。
排他制御に入る前に対象のオブジェクトにロックをかけて、ロック部分の処理が終わったらロックが解除されて、待っていた処理が再開されます。
一連の処理を`排他ロック`といいます。

lockステートメントを使った処理は以下のようになります。
countがNになるまでその部分にロックをかけてNまで到達したらロックを解除して。Console.WriteLineでコンソール画面に文字列を出力させます。

```c#
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        const int N = 1000000;
        var count = 0;
        var sysncObject = new object();

        Parallel.For(0, N, i =>
        {
            lock (sysncObject)
            {
                var tmp = count;
                count = tmp + 1;
            }
        });
        Console.WriteLine($"count: {count}, N: {N}");
    }
}
```

## 最後に
非同期処理って厄介なイメージが自分の中ではありますね。
JavaScriptで処理を書いてた時も仕組みを理解していなかったのもすごく苦労した記憶があります。
今日は非同期処理の内容を再確認できて勉強になりました。
入門書は読み終わったので、明日からVisual C#アプリを作成していきます。
今のところ当初予定していた進捗通りで進んでいるので一安心です。
久しぶりに技術書を駆け足ではありますが1冊通して読みました。疲れたー。
plotlyで2軸の散布図

　前回、ラズパイ4のCPUクロックとCPU温度をゲージを使って描画しました。ここでは、任意の時間ロギングした値を散布図で描画します。
![2021-12-08 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/d2d315ab-3e99-7cfe-1608-fb93210aeb67.png)


###環境

- ラズパイ4　5.10.63
- Python　3.9.2
- plotly　5.4.0

###ロギングのプログラム

```python
import subprocess
import time
import datetime

f = open('/home/pi/dat01.txt','w')
for i in range(1,100):
    clk = subprocess.getoutput("vcgencmd measure_clock arm").split('=')
    CLK = int(clk[1])/1000000.0

    temp = subprocess.getoutput("vcgencmd measure_temp").split('=')
    T = float(temp[1].split("'")[0])

    dt_now = datetime.datetime.now()
    writeData = dt_now.isoformat()+","+str(round(CLK,1))+","+str(T)
    print(writeData)
    f.write(writeData+"\n")
    time.sleep(1)
f.close()
```

　データの一部です。時間、クロック、温度をカンマで区切っています。

```
2021-12-08T05:08:37.515027,1500.3,43.3
2021-12-08T05:08:38.555517,600.1,42.3
2021-12-08T05:08:39.591100,700.2,42.8
2021-12-08T05:08:40.635839,700.2,42.8
```

###ポイントは2軸表示と個々のスケール
　時間はx、クロックをy1、温度をy2のリストに割り当てています。
　2軸表示はsubplotsを利用します。二つの軸のスケールは自動調整されますが、見やすくありません。
![2021-12-07 (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/a10f28fe-96d3-cb73-a649-9ea7c57dd599.png)
　左側のスケールは、update_layoutのrange = [500,1600]を記述して下限と上限を設定できますが、右側のスケールを制限する方法が見つかりませんでした。右側の軸はsecondary_y=Trueで指定します。
　単独でupdate_yaxesを使うとrangeで下限と上限を設定できることがわかりました。
　横軸の時間は、このplotlyライブラリがうまく処理をしてくれて、わかりやすく表示をしてくれます。

```python
import plotly.graph_objects as go
from plotly.subplots import make_subplots

f = open('dat01.txt','r')
s = f.read().split('\n')
print(len(s))

x=[]
y1=[]
y2=[]
for i in range(1,len(s)-1):
    x.append(s[i].split(',')[0])
    y1.append(float(s[i].split(',')[1]))
    y2.append(float(s[i].split(',')[2]))
print(x)
print(y1)
print(y2)

def scatter(mode, symbol, dash, color, y, name):
    d = go.Scatter(
        mode=mode,
        x=x, y=y,
        texttemplate="(%{x}, %{y})",
        name=name,
        marker=dict(symbol=symbol, color=color),
        line=dict(dash=dash, color=color),
    )
    return d

fig= make_subplots(specs=[[{"secondary_y": True}]])

d1 = scatter(mode="lines+markers", symbol="circle", dash="solid", color="red", y =y1, name="CPU clock")
d2 = scatter(mode="lines+markers", symbol="circle", dash="solid", color="blue", y=y2, name="CPU temp")

fig.add_trace(d1)
fig.add_trace(d2,secondary_y=True)
layout1 = go.Layout(
    yaxis = dict(title="Clock [MHz]", range = [500,1600], dtick=100))

fig.update_layout(layout1)
fig.update_yaxes(title="Temp [`C]", secondary_y=True, range=[30, 80])

fig.show()

```
INotificationSideChannel

## ■モジュールで見つかった重複クラスエラー解消方法

発生したエラー

```
Duplicate class android.support.v4.app.INotificationSideChannel found in modules core-1.7.0-runtime (androidx.core:core:1.7.0) and support-v4-21.0.3-runtime (com.android.support:support-v4:21.0.3)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub found in modules core-1.7.0-runtime (androidx.core:core:1.7.0) and support-v4-21.0.3-runtime (com.android.support:support-v4:21.0.3)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub$Proxy found in modules core-1.7.0-runtime (androidx.core:core:1.7.0) and support-v4-21.0.3-runtime (com.android.support:support-v4:21.0.3)

```

gradle.propertiesに下記を追記することで解消しました。

```gradle.properties
android.useAndroidX=true
android.enableJetifier=true
```


参考

https://www.codegrepper.com/code-examples/java/Duplicate+class+android.support.v4.app.INotificationSideChannel+found+in+modules

https://stackoverflow.com/questions/55909804/duplicate-class-android-support-v4-app-inotificationsidechannel-found-in-modules/55932544
Power BIでテキスト検索「Text Filter」ビジュアル

## はじめに
　Power Appsで、テキストを検索するアプリを作ろうとすると、慣れないうちは「ギャラリーコントロールがあって、テキスト入力コントールをxxxにもってきて、Filter関数でxxx...」とそこそこ手間がかかります。
　Power BIの場合は、カスタムビジュアルから認定ビジュアルを持ってきて、何を検索するか決めるだけなので簡単です。

## カスタムビジュアルはこちら

　視覚化のペインから「・・・」（その他のビジュアルの取得、俗に「3点リーダー」とも呼ばれますね）を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/fe5373c1-6ba3-c307-815c-043f4c99ba51.png)

　2021/12にプレビューが始まった新しいフォーマットのペインでも同様の位置です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/4e708f0c-565b-7dff-a750-a4fef1dfe74d.png)

- [Introducing the New Format Pane (Preview) | Microsoft Power BI ブログ | Microsoft Power BI](https://powerbi.microsoft.com/ja-jp/blog/introducing-the-new-format-pane-preview/)
- [The new Format pane in Power BI Desktop (preview) - Power BI | Microsoft Docs](https://docs.microsoft.com/ja-jp/power-bi/fundamentals/desktop-format-pane)

メジャーなビジュアルなので、最初のページに出てきますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/e5a8ec7c-8a71-7e82-8347-531ab434a186.png)

この「ｖ」チェックマークがついているものが「認定ビジュアル」で、マイクロソフト公認のものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/b908e37f-3f3b-354b-5b18-247c05479fee.png)

[Power BI ビジュアルを認定させる - Power BI | Microsoft Docs](https://docs.microsoft.com/ja-jp/power-bi/developer/visuals/power-bi-custom-visuals-certified)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/cbfd513b-c8f1-2de4-c285-873ef91bd985.png)

できるだけ、認定マークを目印に選んで使うと安定して利用できますね。

## ビジュアルをインポート

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/325e66c4-38fe-8546-7968-c4118c39962a.png)

　[今すぐ入手する]とすると、こちらにでてきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/636aee05-ad86-c7c3-bc3e-d2de984a0b75.png)

## 配置

　ビジュアルを配置して

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/abd0f516-379b-7a50-1adc-8c1b6937e2bc.png)

検索したい1つのフィールドを入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/c62b2e7f-facb-71e0-6660-5e328ce53d9c.png)

以上ですね。

## 利用

　検索は部分一致です。検索キーワードとして入力された文字列がフィールドあれば、もれなく検索します。
Power BIの基本昨日であるクロスフィルターが機能しますので、他に配置したビジュアルがフィルターされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/a966e89c-a295-9142-440a-e7d68f19aba8.png)

## おわりに
　Text Filterは便利なので、本当によく使います。カスタムビジュアルは、他にもたくさんあります。よいビジュアルを探して試すのも楽しいですね。
アンチパターン「野戦病院」

[2021.11.30 にtwadaさんの講演を聴く機会がありました](https://twitter.com/e99h2121/status/1465494542841352196?s=20)。
昨日、一昨日もそう書いています。そろそろ感想文は一区切りです。
さて講演の内容の本筋そのものではない甚だ一端なのですが「野戦病院」という描写がありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/9591c497-1871-a360-77d8-9840c1e09fa0.png)
引用: https://speakerdeck.com/twada/quality-and-speed-2020-spring-edition?slide=26



## Long, long ago.

既視感
セッションを聞きながら感じた、ためらいのない私の感想です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/850f96cc-367e-0e65-974b-731b7f18e7a9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/c798bc7c-5ac3-514b-f98e-01afd3e492bd.png)

見たことある。以下が私の脳内の記憶を手書きしたものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/6ff3d332-f97c-f0a9-f55a-4d0076da3b09.png)
朝9時位でしょうか、新人さんが出社するこんな病床。...やめろ。


### 「野戦病院」を知る者のコメントを問題のなさそうな範囲で抜粋

> - まさにプロダクト開発
- よく見た光景だこれ
- **野戦病院ｗ**
- 入社したときこんな感じでしたね
- 20年前こんなだったｗ
- 野戦病院なう
- 在宅に終電はない
- 現場で寝るスキルｗ
- ｗｗｗ椅子の並べ方ｗｗｗ
- 「様々な現場で寝るスキルが育つ」ｗｗｗｗ
- エアーベッド置いてあったな
- 寝袋も置いてありましたね
- 腰痛めるので恒常化は危険です。
- 椅子は3つ並べると寝やすい
- (昔は会議室取り合ってたような…)


## Now.

おじさんの武勇伝というのは大概、ただただ恥ずかしいものです。でも良いのです。だから、「[失敗を披露し、過去を笑える者は強い]([失敗を披露できるひとは強い - Qiita](https://qiita.com/e99h2121/items/76ce33317b8cebf3d713))」と昨日書いたのです。

ここでキャリアを語るのはなかなか難しいですが、昔を知りつつも、自分は今はすっかりこんな働き方をしています。[リモートワークの目次 2021.11 - Qiita](https://qiita.com/e99h2121/items/f5e9ddef0141fe00259b) 

リモートワークの時代。物理的にも足の生えた机はもう過去のものとなりました。でも同じです。**「育児中エンジニア」を代表とする、制約のあるメンバーに優しい働き方を職場皆で考えることは、「皆が」 働きやすくなることに繋がる、というメリットがあります。**

良い話: [育児していたからこそエンジニアのお仕事に役立ったこと10選 - Qiita](https://qiita.com/e99h2121/items/db7e54c111ffcd3c3957)

DX Criteriaにも[リモートワークやフレックスタイムなどの柔軟な働き方を導入しているか を問う項目がありました。](https://dxcriteria.cto-a.org/37a3f1df73d244b7baf6541321a20e02) 過去の遺産は笑い倒して2022年を迎えたいです。明日は電話について考えます。

以上です～。
MySQL8 dumpをcsv形式で出力しようとしたところエラーが発生した

# 目的

- MySQLにて`$ mysqldump`コマンドを使ってdumpファイルをcsv形式で出力しようとしたところエラーが発生したので解決方法をメモ的にまとめる

# エラー内容

- エラー内容を下記に記載する。

    ```
    mysqldump: Got error: 1290: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement when executing 'SELECT INTO OUTFILE'
    ```

- こちらのエラーはおそらくMySQLのセキュリティ系の内容のアップデートによるものようだ。
- 当該の設定に「NULL」が入っているとNG、何も設定されていない状態じゃないとcsvでエクスポートはできないようだ。
- MySQL 5.7.16より前のバージョンだとこのエラーはでないっぽい。

# 環境

- ハードウェア環境

| 項目 | 情報 |
| --- | --- |
| OS | macOS Big Sur(11.6) |
| ハードウェア | MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) |
| プロセッサ | 2 GHz クアッドコアIntel Core i5 |
| メモリ | 32 GB 3733 MHz LPDDR4 |
| グラフィックス | Intel Iris Plus Graphics 1536 MB |

- ソフトウェア環境

| 項目 | 情報 | 備考 |
| --- | --- | --- |
| MySQLバージョン | 8.0.21 for osx10.13 on x86_64 | Homebrewを用いてこちらの方法で導入→[Mac HomebrewでMySQLをインストールする](https://qiita.com/miriwo/items/ef6a064bc545fbea29d9) |

- ※ MySQLはHomebrewを用いてMacのローカルに直接立てている。

# 回避方法

1. 現状の確認
    1. `secure_file_priv`値がよろしくないらしい。当該の値の情報はMySQLのドキュメントの下記に記載されていた。
        1. [https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_secure_file_priv](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_secure_file_priv)
        1. 英語版

            ![Cursor_と_MySQL____MySQL_5_7_Reference_Manual____5_1_7_Server_System_Variables.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/67864737-79a0-e9c8-4834-27a65b70f26c.png)

        1. 日本語翻訳

            ![Cursor_と_MySQL____MySQL_5_7_Reference_Manual____5_1_7_Server_System_Variables.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/c960e58d-46df-2c76-ce1a-d2073f601435.png)

    1. 下記のSQL句を実行して`@@global.secure_file_priv`の値が現在どのようになっているかを確認する。

        ```sql
        mysql> select @@global.secure_file_priv
        ```

    1. 下記のように出力された。やはり「NULL」に設定されているようだ。

        ```
        +---------------------------+
        | @@global.secure_file_priv |
        +---------------------------+
        | NULL                      |
        +---------------------------+
        ```

1. 情報の修正
    1. 当該の`secure_file_priv`の値はどうやら動的に更新することが不可能なようである。
        1. 下記の記事を参考に設定ファイルの変更とMySQLの再起動を行い問題が解決した。
            1. [https://qiita.com/bohebohechan/items/207e87786b1e30f60abe](https://qiita.com/bohebohechan/items/207e87786b1e30f60abe)
MySQL csv形式でdumpを出力する

# 目的

- MySQLでdumpをcsv形式で出力する方法をまとめる

# 方法

- 権限があるMySQLユーザーを用いて下記コマンドを実行する。

    ```mysql
    $ mysqldump -u MySQLユーザー名 -p --tab="ファイルを出力したいディレクトリのフルパス" --fields-terminated-by=, DB名
    >password: MySQLユーザーのパスワード
    ```

- 下記のエラーが出る場合はこちらの記事を参考に解決できた→[https://qiita.com/bohebohechan/items/207e87786b1e30f60abe](https://qiita.com/bohebohechan/items/207e87786b1e30f60abe)

    ```
    mysqldump: Got error: 1290: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement when executing 'SELECT INTO OUTFILE'
    ```

- MySQLのユーザーのパスワードを失念してしまった場合はこちらの記事を参照→[Mac ローカル環境の MySQL 8.x のrootパスワードを忘れた時のリセット方法](https://qiita.com/miriwo/items/1880e9d2ebcfd3c0e60ds)

# 参考文献

- [7.4.3 mysqldump による区切りテキストフォーマットでのデータのダンプ](https://dev.mysql.com/doc/refman/5.6/ja/mysqldump-delimited-text.html)
- [https://qiita.com/bohebohechan/items/207e87786b1e30f60abe](https://qiita.com/bohebohechan/items/207e87786b1e30f60abe)
- [https://mugendennou.net/mysql/9troubleshooting/errorcsvoutfile/](https://mugendennou.net/mysql/9troubleshooting/errorcsvoutfile/)
モバイルゲームアプリのパフォーマンステスト

#はじめに
この記事は、グリー品質管理Advent Calendar 2021の12日目になります。
筆者がモバイルゲームアプリ（以下アプリ）の新規リリースに携わった際、クライアント側のパフォーマンス計測の依頼を対応した備忘録を記録していきます。
同じような対応をされる方の参考になれば幸いです。

#パフォーマンステスト実施の経緯
私の担当しているアプリでは、比較的リッチなホーム画面演出や3Dモデルを採用しており、起動後のロード時間や低スペック端末でのFPS値の低下が課題として存在していました。
しかし、あくまでプロダクトチーム内での体感値が元となっていたため、改善の具体的な目標を定義するため、定量的な数値を計測し、他アプリとの比較を行うこととなりました。
#パフォーマンステストの実施
今回アプリでパフォーマンステストを実施した際の一連の流れを記載していきます。
##要件定義
テストの要件をプロダクトチームとすり合わせます。
###計測箇所調整
アプリ内でパフォーマンスを計測する箇所を具体的に定義します。
プロダクトチームの要望をヒアリングしたり、機能テストやユーザテストの結果をもとに計測箇所を決めるとよいと思います。
私の担当タイトルでは、次のような箇所をテストしました。

- アプリ起動からロードを挟み、ホーム画面遷移までの所要時間
- バトル開始ボタンタップから、実際にバトル画面へ遷移するまでの所要時間
- インゲーム/アウトゲーム各画面のFPS値

注意点としては、テストの条件をすり合わせておくことが挙げられます。
ゲームの進行状況やキャラクター編成によって描画するオブジェクトの量が増えたりする場合、ロード時間やFPSに影響を及ぼすかを確認し、必要に応じて複数パターンのテスト項目を設ける必要があります。
###計測端末調整
アプリリリース時の推奨端末や下限端末、市場シェア等をもとに計測対象とする端末を選定しましょう。
Android端末は種類が膨大にあるので、メーカーやチップセット、RAMサイズの組み合わせで範囲を絞っていく必要があります。
海外でのリリースを行う場合はAndroidの低スペック帯を厚めにする、MediaTek製チップセットを盛り込む等、アプリのターゲットを考慮して調整できるとよいと思います。
###成果物の定義
計測結果をどのような形で提出するかを決めておきます。
例えば私が対応したケースでは、シートへの計測結果記載＋計測時の動画提出を行い、
数値での定量的な指標と、動画を見た際の定性的な指標をもとに、プロダクトチームにパフォーマンス改善の指針を定めてもらいました。
##テスト準備
すり合わせた要件に沿ったテストを実施する上での事前準備で実施したタスクを記載していきます。
###テストスケジュールの策定
通常のテストでもスケジュールは引くと思いますが、後述の各タスクを調整する必要があるので、普段より早めにスケジュールは調整する必要があります。
###デバッグ機能/ダミーデータの準備
FPSの計測や、テストの条件を満たすために必要なデバッグ機能やダミーデータの準備をプロダクトチームに対応してもらう必要があります。
今回はFPS値の表示や、検証に必要な条件を整えるためのデバッグコマンド（ホーム画面のオブジェクトを増やす）を準備してもらっています。
###端末準備
事前に決定したテスト対象端末の準備です。
###計測結果記載シートの作成
テスターさんが計測結果を記入するシートです。
事前にどんな端末で何を計測するかは決めてあると思うので、それらの項目が網羅されていればOKだと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320947/ce4a133f-03d7-9906-5c58-bae0aeb13956.png)

:::note info
検証シートの作成例です。
アプリ起動からホーム画面までの遷移を以下の条件で検証しました。
・最小限の範囲をプレイした状態
・ある程度ゲームを進行させて、ホーム画面の処理を増やした状態
端末はチップセットのメーカーや、RAMサイズでパターンを分けています。
:::

##テスト実施
基本的にはここまで準備ができていれば、テスト実施の上で困ることはない…はずです。
##結果報告～改善後の再実行
今回のゴールは、パフォーマンス改善の目標値を定義し、改善を実施するところまでとなっています。
そのため、結果の報告後はプロダクトチームによる改修結果の再確認が必要となります。
今回の自社タイトルの例では、プロダクトチームが考える対象スペックの計測数値を他タイトルの計測数値を比較し、画面遷移の所要時間や低FPS状態の発生が浮き彫りになりました。
その結果、**ホーム画面への遷移時間の改善と、低スペック端末向けのFPS改善のための低画質モード**が実装されました。

#今後の展望
パフォーマンステスト以外の多端末での動作テストでも言えることではあるのですが、Androidはスペックの幅が広く、手広くやろうとするとテスト実施工数の増加が課題となってきます。（端末の確保も大変ですが…）
これらのテストは実施内容をあらかじめスクリプト化しておくことが可能なので、Airtest等のUIテストツールを使用することで、工数の削減余地があるのではないかと考えています。
今後の目標として、内製でのスクリプト作成や、UIテストに強いテストベンダーさんと連携して、効率的にテストを実施する方法を模索しています。

進捗があれば記事をアップデートしていきたいと思います！
[Firebase][iOS] メールアドレス更新前に確認メールを送る

Firebase Authentication のユーザーのメールアドレスを更新する方法として、
[「新しいメールアドレスに認証メールが送られ、リンクがクリックされると更新される」関数](https://firebase.google.com/docs/reference/swift/firebaseauth/api/reference/Classes/User#sendemailverificationbeforeupdatingemail:completion:)があります。

```swift:
// newEmail に確認メールが送信されます。メールのリンクをクリックすると newEmail に変更されます。
Auth.auth().currentUser?.sendEmailVerification(beforeUpdatingEmail: newEmail) { error in
    // ...
}
```

ちなみに、以下のコードだとメール認証なしで変更されます。

```swift:
Auth.auth().currentUser?.updateEmail(to: email) { error in
    // ...
}
```

**追記(2021/12/12)**
エミュレータを使用中は、``sendEmailVerification(beforeUpdatingEmail:)``をすると内部エラーが返ってくるみたいです。
Angular v13 を Cloudflare Pages にデプロイする

これは [Angular Advent Calendar 2021](https://qiita.com/advent-calendar/2021/angular) の８日目の記事です。
現在の時刻は８日１時です。当日に書き始めるなんて意味がわかりません。計画性は何処へ（
# Cloudflare Pages って？
Cloudflareが提供している、JAMstackプラットフォームです。静的サイトのホスティングをするのに使えます。本稿執筆時点では無料枠での制限が緩めなのでおすすめです。ただ、ビルド前の環境の初期化に割と時間がかかります。
基本的に、GUIに従っていくだけで導入することができます。
# Angular v13 のプロジェクトに導入する際に追加でやること
ただ従うだけではダメで、少々手を加える必要があります...
## 1. Node.js のバージョンを明示する

:::note info
<a href="https://github.com/nvm-sh/nvm" rel="nofollow noopener" target="_blank">nvm</a>や<a href="https://github.com/nodenv/nodenv" rel="nofollow noopener" target="_blank">nodenv</a>を使用している場合は後述のファイルが自動で生成されるので、この手順は不要です。
:::

`.nvmrc`もしくは`.node-version`というファイルを作成し、開発環境で使用中のNode.jsのバージョンを記載しておく必要があります。`16.13.1`のような感じです。
現時点ではCloudflare Pagesのデフォルトの Node.js のバージョンは`12.18.0`[^1]となっていて、Angularの要件`12.20.0`[^2]より古いので動かないのです。

:::note warn
Cloudflare側の環境変数<code>NODE_VERSION</code>で指定することもできますが、その場合は本番環境とプレビュー環境の両方に変数を書かなくてはならないことに注意してください。
:::
[^1]: https://developers.cloudflare.com/pages/platform/build-configuration#language-support-and-tools
[^2]: https://github.com/angular/angular/blob/13.0.x/package.json#L11

## 2. outputPath を変更する
![IMG_3930.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/582081/7c2914ac-9acb-69f9-b75d-c578d87a6a72.jpeg)
ビルド コマンドを
`ng build --output-path=./dist`
に変更します。
`angular.json`の`outputPath`で指定されている出力先がデフォルトでは`dist/<プロジェクト名>`になっているためです。

# おわりに
簡単にできるだろうと思っていざやり始めるとうまくいかなくてびっくりしました。
知っていればなんてことないので、この記事が誰かの役にたてば幸いです。
ソースコードの整形

Scratchエディタには、ソースコードを綺麗に整形する機能が用意されています。何もないところで右クリックをして「きれいにする」を選んでください。

<img width="75%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/42cb828d-ebe9-5bf7-cb02-83a2d2f027db.png">

するとブロックが、縦一列に整形されます。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/40ba7746-d951-a25d-2915-046ae0e8a94b.png">

個人的な感想ですが、Scratchエディタはスペースの関係上どうしても縦長になりがちなことを考慮すると、縦横に隙間なくレイアウトするよりは、このように縦一列にブロックが並んでいた方が、他の人にとって詠みやすいコードになると思います。あと「きれいにする」を実行すると、どこかにあった使われていないゴミのようなブロックも、探しやすくなります。
React 備忘録

https://prog-8.com/

##使い方
* インポート
* クラス定義
* メソッド定義（戻り値 jsx）
* エクスポート

```javascript
import React from 'react';
class App extends React.Component {
  render() {
　　const text ="定数";
　　return (
       {/*jsx*/}
    );
  }
}
export default App;
```

https://www.youtube.com/watch?v=gLbTluYSb_U&t=196s
##JSXとは
* JavaScriptの拡張言
* HTMLライクな記述
* 拡張子.jsx / js 

##階層構造で書く
###React.Fragment
HMTLタグとして出力されない

```jsx
return(
  <React.Fragment>
   <p>React入門</p>
   <p>React入門</p>
  </React.Fragment>
)
```
###React.Fragmentの省略形
```jsx
return(
  <>
   <p>React入門</p>
   <p>React入門</p>
  </>
)
```
###【エラー】非階層構造：Pタグの並列
```jsx
return(
  {/*コメントはこう書く*/}
  <p>React入門</p>
  <p>React入門</p>
)
```
Prezto をやめてイチから .zshrc をカスタマイズした

## はじめに

PC 新調を機に、導入することで色々なオプションやエイリアスが設定されるがあまり使いこなせていなかった Prezto をやめてフレームワークを使わず、必要な機能だけに絞って `.zshrc` をカスタマイズしてみました。
その際に便利だと思った内容をピックアップしてご紹介します。

## ディレクトリ移動関連

```
setopt auto_cd     # cd を省略してディレクトリを移動
setopt auto_pushd  # cd 時に直前のディレクトリをスタックに追加する スタックは cd -<TAB_KEY> で呼び出せる
DIRSTACKSIZE=50    # スタック数の上限

setopt pushd_ignore_dups  # 重複するディレクトリをスタックに追加しない
setopt cd_silent          # cd - や cd -<TAB_KEY> でディレクトリを移動した後にカレントディレクトリを出力しない
```

## 補完関連

```
# 補完を拡張する https://github.com/zsh-users/zsh-completions
if type brew &>/dev/null; then
  FPATH=$(brew --prefix)/share/zsh-completions:$FPATH
fi

autoload -Uz compinit && compinit -i  # 補完を有効にする

zstyle ':completion:*' menu select  # Tab で選択中の項目をハイライト

# 大文字小文字を無視し、部分一致でマッチングして補完する
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
```

## ヒストリ関連

```
HISTFILE=~/.zsh_history
HISTSIZE=10000  # メモリ上にキャッシュする上限
SAVEHIST=50000  # HISTFILE に書き込まれる上限

setopt append_history        # 複数のセッションで使用する時などにヒストリを置き換えないようにし、追加を行うようにする
# setopt inc_append_history  # 入力されるとすぐにヒストリに追加する share_history と排他的な設定
setopt share_history         # inc_append_history に加えて他のセッションで追加されたヒストリを利用できるようになる

setopt hist_ignore_all_dups  # ヒストリにあるコマンドと重複する場合に古いコマンドはリストから削除する
setopt hist_ignore_space     # コマンドラインの最初の文字がスペースである場合、ヒストリからコマンドを削除する

bindkey "^p" history-beginning-search-backward  # コマンド入力中に Control + p でヒストリから後方に補完候補を検索して補完する
bindkey "^n" history-beginning-search-forward   # コマンド入力中に Control + n でヒストリから前方に補完候補を検索して補完する
```

コマンド入力中のヒストリ検索補完は特によく使う
https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#:~:text=history%2Dbeginning%2Dsearch%2Dbackward

コマンド入力中の補完後にカーソル位置が末尾に移動しないのが不便に感じる場合はこちら

```
autoload history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^p" history-beginning-search-backward-end
bindkey "^n" history-beginning-search-forward-end
```

https://github.com/zsh-users/zsh/blob/master/Functions/Zle/history-search-end

## 入出力関連

```
setopt correct  # スペル修正を提案する
setopt interactivecomments  # インタラクティブシェルでもコメントを許可する
```

## プロンプト

```
# Git の情報を表示する
autoload -Uz vcs_info
precmd_vcs_info() { vcs_info }
precmd_functions+=( precmd_vcs_info )
setopt prompt_subst
zstyle ':vcs_info:git:*' check-for-changes true          # staged/unstaged の反映を有効にする
zstyle ':vcs_info:git:*' stagedstr '+'                   # staged がある場合に表示する %c に対応
zstyle ':vcs_info:git:*' unstagedstr '*'                 # unstaged がある場合に表示する %u に対応
zstyle ':vcs_info:git:*' formats '%F{008}%b%u%c%f' .     # 通常時のフォーマット $vcs_info_msg_0_ でプロンプトに表示する
zstyle ':vcs_info:git:*' actionformats '%F{008}%b%u%c%f | %a'  # コンフリクトが発生した時などはこちらが表示される %a は発生したアクション名に対応

PROMPT='
%F{032}%~%f $vcs_info_msg_0_
%F{magenta}%B❯%b%f '
```
![スクリーンショット 2021-12-08 1.50.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266269/79d85f85-2966-c08b-4d47-f750c7d0d2ca.png)

プロンプトは untracked files の状態表示などに対応しておらず、関数を作成していく必要がありそうです。
https://zenn.dev/kaityo256/articles/zsh-vcs-prompt

このあたりは Prezto を使うと手間をかけずにいい感じに表示できて楽なんですが、手間をかけて少しずつ便利にしていくのも楽しいものですね。
また弄っていきたいと思います。
STM32F303K8とMbedでI2Cスレーブで遊ぶ(受信)



目的
I2Cスレーブのテスト


![o_con135.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/e2c7b46e-9249-8dbd-06b1-9cb5847c6487.jpeg)


参考


https://os.mbed.com/users/okini3939/notebook/i2cslave_jp/



```c++


#include "mbed.h"

//GPIOの初期化
DigitalOut myled(PB_3);//LED1

//シリアルの初期化
Serial pc(SERIAL_TX, SERIAL_RX); //nucleo 303

//I2Cの初期化
I2CSlave slave(I2C_SDA, I2C_SCL); //nucleo 303

//メイン関数
int main()
{

    char buf[10]; //I2Cバッファー

    //I2Cスレーブのアドレスの設定
    slave.address(0x80);

    //無限ループ
    while(1) {

        //I2Cの状態の読み出し(ポーリング)
        int i = slave.receive();

        //ステータスの表示
        pc.printf("I2C Status: %x\r\n", i);

        if (i == I2CSlave::WriteAddressed) {

            buf[0] = 0;//バッファーのクリア

            //I2Cスレーブの受信バッファーを読み取る
            slave.read(buf, 1);

            //I2Cスレーブの受信データの表示
            pc.printf("\tRead A: %d\r\n", buf[0]);

        }//if

        //LEDの点滅
        myled = 1;
        wait(0.1);
        myled = 0;
        wait(0.1);

    }//while

}//main




```


I2Cマスター STM32L010F4P6



```c++


//I2C_OUT_TEST1

#include "mbed.h"

//I2Cアドレス
const int ADDR = 0x80; //  address

//I2Cの構造体
I2C_HandleTypeDef hi2c1;

//I2Cの初期化
void I2C_INIT1()
{
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_I2C1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    __HAL_RCC_I2C1_CLK_ENABLE();

    //I2C_HandleTypeDef hi2c1;

    hi2c1.Instance = I2C1;
    hi2c1.Init.Timing = 0x00000708;
    hi2c1.Init.OwnAddress1 = 0;
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c1.Init.OwnAddress2 = 0;
    hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    HAL_I2C_Init(&hi2c1);

    HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE);

    HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0);

}//I2C_INIT1

//GPIOの初期化
void GPIO_INIT1()
{
    
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}//GPIO_INIT1

//メイン関数
int main()
{
    wait_ms(2); //HAL_Delay()になぜか失敗するので呼び出す

    //I2Cの初期化
    I2C_INIT1();

    //GPIOの初期化
    GPIO_INIT1();

    //無限ループ
    while (1) {

        //I2Cに1を送信
        HAL_I2C_Master_Transmit(&hi2c1,ADDR, (uint8_t *)"\001", 1, 3000); //data 1
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
        HAL_Delay(1000);//1秒待つ

        //I2Cに1を送信
        HAL_I2C_Master_Transmit(&hi2c1, ADDR, (uint8_t *)"\002", 1, 3000); //data 2
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
        HAL_Delay(1000);//1秒待つ

    }//while

}//main





```
AWS CLIで Web サイトを構築、管理、運用する(8日目)

8日目です！昨日、書き忘れましたが１週間経ちました！

ということで（どういうこと？）、アクセス時のエラーページをカスタムエラーページにします。
通常は、指定したファイルがない場合などには、以下のような XML が HTTP403 とともにかえってきます。

```XML
<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>AccessDenied</Code>
  <Message>Access Denied</Message>
  <RequestId>****************</RequestId>
  <HostId>/**********/******************************</HostId>
</Error>
```

これを変えてみます。

# 8日目の要約
アクセスエラー時の表示ページをカスタムページにします。

# AWS CLI の準備
このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。
なんなら、 AWS CloudShell で実行するのも楽でよいと思います。
この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。

バージョン１
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html

バージョン２
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html

# 概要
CloudFront のエラーページの設定を変更して、カスタムエラーページを返すようにするよ！

# さあ、やってみよう！
## エラーページを作る
カスタムエラーページ用の HTML ファイルを作成します。

```HTML:error.html
<!DOCTYPE html>
<html lang="ja">
<meta charset="UTF-8">
<head>
  <title>えらーです m9('ω')</title>
</head>
<body>
  <h1>残念〜！</h1>
  <h2>アクセスできませーん！</h2>
</body>
</html>
```

・・・煽ってますねぇ。。。

作成できたら、 s3 cp コマンドで S3 バケットにアップロードしておきます。

```sh
aws s3 cp error.html s3://<バケット名>
```

## CloudFront の設定変更を準備する

やはり、基本的には、[6日目](https://qiita.com/hirosys/private/c339bf0d85d4a8ac74e5#cloudfront-%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B%E6%BA%96%E5%82%99)で実施したものと似た内容です。

1. cloudfront get-distribution-config コマンドで現状の設定を取得する
1. ETag の値を確認する
1. 取得した json ファイルに対して、設定変更を加える
1. cloudfront update-distribution コマンドを実行して更新する

この記事では、3と4についてのみ記載します。１と２の実施内容については、[6日目](https://qiita.com/hirosys/private/c339bf0d85d4a8ac74e5#cloudfront-%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B%E6%BA%96%E5%82%99)の記事をご確認ください。

### 取得した json ファイルに対して設定変更を加える
CustomErrorResponses 内にある、 ```"Quantity": 0``` を削除します。
この削除箇所に関しては、たんに「設定がゼロ個ですよ」という意味の行です。

```json:削除箇所
  "CustomErrorResponses": {
    "Quantity": 0      <--- この行だけ削除
  },
```

削除した場所に、以下を貼り付けます。
オリジンへアクセスした際、 HTTP403(Access Denied)が返ってきたら error.html を返す設定です。

```json:貼り付け内容
    "Quantity": 1,
    "Items": [
      {
        "ErrorCode": 403,
        "ResponsePagePath": "/error.html",
        "ResponseCode": "403",
        "ErrorCachingMinTTL": 10
      }
    ]
```

## cloudfront update-distribution コマンドを実行して更新する
準備ができたら、6日目と同様に cloudfront update-distribution コマンドを実行して、設定変更を行います。

```sh
aws cloudfront update-distribution --id <CloudFront Distribution ID> ¥
--cli-input-json file://distribution_error.json ¥
--if-match 確認したETag の値
```

コマンド実行に成功すると、Distribution の内容が記された json が返されます。
やはり長いので割愛します。

## 動作確認
確実に存在しない URL を指定して curl コマンドを実行します。

```sh
curl https://<ドメイン名>/naiyo.html
```

CloudFront 側でデプロイが完了していれば、以下のように指定したカスタムエラーページが返ってきます。

```HTML:error.html
<!DOCTYPE html>
<html lang="ja">
<meta charset="UTF-8">
<head>
  <title>えらーです m9('ω')</title>
</head>
<body>
  <h1>残念〜！</h1>
  <h2>アクセスできませーん！</h2>
</body>
</html>
```

# まとめ
これまではパス誤りなどで、XML が返ってきてしまっていましたが、カスタムエラーページを返すようにしたことでWeb サイトの統一感ある表示に繋げられると思います。

次回は少し、セキュリティ設定を見直したり導入したりしようと思います。

* 今回使ったコマンド
 * s3 cp
 * (cloudfront get-distribution-config)
 * cloudfront update-distribution
Laravel のルーティングファイルを web や api 以外に増やす

この記事は、[ゆめみ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/yumemi) の7日目の記事です（もう過ぎちゃったけど）

Laravel でデフォルトで用意しているルーティング定義ファイルとして `api.php` と `web.php` があります
ユーザー定義のルーティング定義ファイルを増やす方法を紹介します

# 前提条件

確認したバージョン

- PHP 8
- Laravel v8.x

# やり方

参考に実装してみた Pull Request の Files changed を見るのが手っ取り早いです

https://github.com/ikasam/laravel-boilerplate/pull/1/files

参考実装に従って、ヘルスチェック用のルーティングファイルを増やす手順を示します

## ヘルスチェック用のルーティングを書くファイルを新しく作る

`routes/health-check.php` とでもします

```php:routes/health-check.php
<?php

use Illuminate\Support\Facades\Route;

Route::get('/health-check', function () {
    return response()->json(['status' => 'ok']);
});
```

クロージャで適当な JSON を返しておきます

## RouteServiceProvider でルーティングを登録

`boot` メソッドに足しましょう

```diff_php:app/Prooviders/RouteServiceProvider.php
public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        Route::prefix('api')
            ->middleware('api')
            ->namespace($this->namespace)
            ->group(base_path('routes/api.php'));
        Route::middleware('web')
            ->namespace($this->namespace)
            ->group(base_path('routes/web.php'));

+       Route::namespace($this->namespace)
+           ->group(base_path('routes/health-check.php'));
    });
}
```

これだけで OK です
ヘルスチェックのエンドポイントを叩けばレスポンスが返ってきます


```console
$ curl -i http://localhost/health-check
HTTP/1.1 200 OK
Date: Tue, 07 Dec 2021 15:41:59 GMT
Server: Apache
Cache-Control: no-cache, private
X-Robots-Tag: noindex, nofollow
Content-Length: 15
Content-Type: application/json

{"status":"ok"}
```

# メリット

- ヘルスチェックのためだけのルーティングが、アプリケーションのルーティングに混ざらなくて良い
- `web` や `api` のルーティンググループで通している Middleware を通らないので、無駄な処理が発生しない
  - 例えば、セッションが生成されたりしない [^session]
- その気になればちょっとした処理も書ける
  - バックエンドのデータストアとの接続性を確認したり
  - データストアからメンテナンス状態を確認して、メンテナンスモードのレスポンスを返すなど
- がんばればテストも書ける
- PHP や Laravel のブートストラップまでは正常に動くことが確認できる

# デメリット

- 単に Web サーバーが生きているか確認するだけなら、 public に静的ファイルを置くだけでよい
- web や api　で動かしている Middleware が生きてるかは確認できない
  - RouteServiceProvider での登録時にヘルスチェックで確認したいものを Middleware group 指定すればいいけど
- フレームワークのバージョンが上がって、 RouteServiceProvider の書き方が変わったら移植する必要がある
  - 6.x → 8.x で結構変わってた
- ルーティングの重複に気づきづらい
  - API に行って欲しいけど一生ヘルスチェックに吸われるとかがあるかも

# まとめ

- 気軽にルーティングファイルを生やしていきましょう
  - アプリケーション内のある程度の単位で別けるのも、スッキリして良さそう
- ただし、やりすぎると混乱するのでほどほどに

[^session]: AWS ELB のヘルスチェックで無限にセッションが生成される地獄を経験された方なら嬉しさがわかるかも
VMware Cloud Disaster Recovery (VCDR) アップデートまとめ (2021/12/6時点)

最近(2021/12/6)でアナウンスされたVMware Cloud on AWS 向けDRソリューション VMware Cloud Disaster Recovery (VCDR) について、たくさん嬉しいアップデートがあったのでメモ。

#1. そもそもVMware Cloud Disaster Recoveryって？
VMware Cloud on AWS向けのDRソリューションです。巷ではVCDRとよく略称で呼ばれます。日本では2021年1月に東京リージョンでサービス提供開始されたDR (災害対策)ソリューションです。VMware社のブログでもやさしく解説されていますが、クラウドならではのメリット (必要な時だけデプロイ) を活用できる素敵なソリューションです。

https://vmware-juku.jp/solutions/cloud-disaster-recovery/


[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864151/81a2c18e-516d-ede0-d115-18011b3560a8.png)](https://www.vmware.com/products/cloud-disaster-recovery.html)


#2. そういえばVMware Cloud on AWSって？
オンプレとクラウドのそれぞれに強みを持つ両社が共同開発したクラウドサービスです。Amazon EC2ベアメタルインスタンス上にVMware仮想環境が構築されています。両社の公式サイトも参考にどうぞ。

https://www.vmware.com/jp/products/vmc-on-aws.html

https://aws.amazon.com/jp/vmware/


#3. VMware Cloud Disaster Recovery (VCDR) アップデートまとめ (2021/12/6時点)
つい最近アップデートがありましたのでいくつか抜粋してまとめます。

### File-level Recovery

ファイル単位のリカバリーに対応しました。今までは最小でもVM単位のリカバリー(災害発生時からの復旧)だったので、ファイル単位でリカバリーできるというのは使い勝手が非常に良くなりますね。

### Protect workloads running on VMware Cloud on AWS SDDC with low RPOs

VMware Cloud on AWS から VMware Cloud on AWS (例えば東京リージョンから韓国ソウルリージョン)に対してDR構成を組むことは以前からできたのですが、最小RPOは4時間でした。それが今回のアップデートで最小RPOが30分に短縮されました。より厳しいDR要件にも対応できるようになりますね。 (ちなみに2021/9/3のアップデートでは、オンプレミスvSphere仮想環境からVMware Cloud on AWSへのDR構成で最小RPOが4時間から30分となっていたので、それが拡張された形になります。)

### AWS Asia-Pacific (Osaka) region

大阪リージョンのVMware Cloud on AWSは今までVMware Cloud Disaster Recovery (VCDR)に非対応でしたが、今回のアップデートで対応するようになりました。日本のユーザーにはこちらも嬉しいアップデートですね。


### ほかにもたくさん..!
上記で紹介した以外にも今回たくさんのアップデートがありました。
本文は英語ですがこちらVMware DocsにVMware Cloud Disaster Recovery についてアップデートがまとまっていますのでご覧ください。

> [VMware Cloud Disaster Recovery Release Notes](https://docs.vmware.com/en/VMware-Cloud-Disaster-Recovery/services/rn/vmware-cloud-disaster-recovery-release-notes/index.html)

> [![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864151/a4b38434-5552-4c74-172d-86b542476fb8.png)](https://docs.vmware.com/en/VMware-Cloud-Disaster-Recovery/services/rn/vmware-cloud-disaster-recovery-release-notes/index.html)



#4. 関連記事

https://qiita.com/sanjushi/items/2696271334893b12a494

https://qiita.com/sanjushi003/items/175aad0ccdbb3488bfda
Colaboratoryで「GitHubにコピーを保存」ができないとき


#問題
GitHub初心者がColaboratoryから[ファイル]->[GitHubにコピーを保存]しようとしたら、できなかった。

[ファイル]->[GitHubにコピーを保存]すると以下のような画面が表示された。
![スクリーンショット 2021-12-08 午前0.43.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2364413/655a5de5-8daf-ec3f-0827-df0be11e6007.png)
このまま「OK」を押して進めようとするも何も起こらなかった…

#解決方法
ブランチが存在しないことが原因だったと思われる。
GitHubでレポジトリー作成時にブランチを作成するのが初心者には簡単。

![スクリーンショット 2021-12-08 午前0.51.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2364413/d60fbb28-f522-39b1-041f-311210d8da71.png)

README file追加にチェックを入れることで解決した。
「This will set main as the default branch. 」の表示でブランチが作成されることを確認できる。

Colaboratoryからもブランチが作成されたことが確認できる。

![スクリーンショット 2021-12-08 午前0.48.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2364413/2d6dabe0-add0-5a81-e895-caa7e004c930.png)

「OK」を押すとGitHubのページに移動してコピーが保存された。

#あとがき
誰でもわかっていることなのかもしれませんが初心者にはなかなかわからなかったので書いておきました。
とりあえずNotebookをpushできるようになりました。
OpenShift 内部のContainer Registryにイメージをpushする。(ImageStreamを作成する)

#### 経緯・課題
OpenShiftのインフラ管理の勉強ついでに、よく使いそうな作法、つまりがちなところを備忘目的でまとめています。
インターネット接続のないCRC環境内の内部registryに、コンテナイメージをpushする際にガイド通りじゃなかったところが多少あり手間取ったので、記載しています。

---
#### 前提・環境など
・ホスト:
　CRC/code ready container 環境 (CentOS Linux release 7.9.2009 (Core))
・クライアント・作業PC:
　MacOS Big Sur (11.6)

---
#### 参考知識
1.[3.3.3. 内部 OpenShift レジストリーへのアクセス]
(https://access.redhat.com/documentation/ja-jp/red_hat_codeready_containers/1.28/html/getting_started_guide/accessing-the-internal-openshift-registry_gsg)
　基本的にはこちらのサイト通りです。一部sudoが必要でした。
2.[router認証](https://github.com/code-ready/crc/issues/775)
　レジストリの手前にいるrouter周りで認証弾かれてしまうらしく、一手間かけました。
　レジストリでpodman login する際に必要
3.podman/またはdockerコマンドは導入済みであること


---
#### やりたいこと
インターネット接続なし(VPNのみ)のCRC環境の内部コンテナレジストリに、特定のコンテナイメージをpushする。

##### 1.レジストリURLの確認
CRC環境にログインする
>$ oc login -u kubeadmin -p \<password> 

registryURLの確認 (openshift-image-registry のnamespaceを指定して、routeが外部公開しているURLを探します。)
> $ oc get route -n openshift-image-registry
(出力例)
NAME            HOST/PORT                                                 PATH   SERVICES         PORT    TERMINATION   WILDCARD
default-route   <i><b>default-route-openshift-image-registry.apps-crc.testing          image-registry</b></i>   <all>   reencrypt     None


##### 2.レジストリへのログイン
レジストリ手前のrouter認証のために証明書をCRC環境から取得します。(sudo で実行)
cp先のディレクトリは、適宜 <b><i>sudo mkdir -p </i></b>などで作成します。
> \$ oc extract secret/router-ca --keys=tls.crt -n  openshift-ingress-operator
$ cp tls.crt /etc/docker/certs.d/default-route-openshift-image-registry.apps-crc.testing/

一度dockerを再起動します。
レジストリへの疎通の確認は以下を実行。
>$ curl -kvs https://default-route-openshift-image-registry.apps-crc.testing

podman login 実施(ガイドにはないですが、sudoをつけます。dockerでも可。--tls-verify=falseもつけます。)
>\$ <i><b>sudo</b></i> podman login -u kubeadmin -p $(oc whoami -t) default-route-openshift-image-registry.apps-crc.testing <b><i>--tls-verify=false</i></b>
(出力例)
Password:
Login Succeeded!



##### 3.imageのpush
ここでは、あらかじめ quay.io/libpod/alpineをpullしているものとします。
namespaceは管理のために作成しておくと整理しやすいです。
>$ oc new-project tcptest

内部レジストリのタグ付けでは、作成済みのnamespaceを指定します。(存在しないnamespaceは指定できないので、先に作っておきます)
>$ podman tag alpine:latest default-route-openshift-image-registry.apps-crc.testing/<b><i>tcptest</i></b>/alpine:latest

push実行
>
$ podman push default-route-openshift-image-registry.apps-crc.testing/tcptest/alpine:latest --tls-verify=false

push結果の確認(image streamの確認)
>
$ oc get is
(出力例)
NAME     IMAGE REPOSITORY                                                         TAGS     UPDATED
alpine   default-route-openshift-image-registry.apps-crc.testing/tcptest/alpine   latest   53 seconds ago

無事内部レジストリにimageをpushできました。
[k8s] コンテナに身に覚えのない環境変数が設定されている…！

kubernetesのお話

先日、アプリケーションの追加機能の動作確認をしていて、どうも思ったように動かない、なんでだ…
と思って色々調べたところ、全く覚えのない環境変数が設定されていて、それがそのアプリケーションで使用する環境変数名で、値がアプリケーションが想定していない形式だったので、それが原因でエラーになっていた
ということがありました
果たしてこいつはどこから来たのか

# 原因

kubernetesのservice discoveryのひとつでした（完全に仕様）
https://kubernetes.io/ja/docs/concepts/services-networking/service/#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0

おそらく一般的に使われているservice discoveryはdnsかと思います
`service-name.namespace.svc.cluster.local` で目的のserviceにアクセスするというやつ

それの他に、環境変数でserviceのipやportを名前解決（？）してにアクセスできるというものがある、ということ
こんな方法があったんですね、全く知らなかった
で、このドキュメントに書かれている例だと

```
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
```

ポート番号がkeyに入ってるやつはまだしも、その他はなかなか汎用的なワードになっていて
`[service名]_SERVICE_HOST` `[service名]_SERVICE_PORT` `[service名前]_PORT` となります
例えばとあるnamespace内に `mysql` という名前のserviceがあるとしたら（あくまで例えば）
そのnamespace内の全てのコンテナに `MYSQL_SERVICE_HOST` `MYSQL_SERVICE_PORT` `MYSQL_PORT` という環境変数が設定されてしまいます
ちょっとそれはどうなの…という感じ
知らなかったらなんかに引っかかってもおかしくないし、知っててもうっかり引っかかりそうです

# 対応など
無効化できます
https://kubernetes.io/ja/docs/concepts/services-networking/connect-applications-service/#service%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%99%E3%82%8B
podの設定に `spec.enableServiceLinks: false` を追加します
deploymentの場合は `spec.template.spec.enableServiceLinks: false` ですね
デフォルト無効にするような設定はないみたいです

コンテナの環境変数定義で同じ名前のものがある場合、そちらが優先されます
環境変数があれば優先だけど、設定がない場合はflag、configファイルからの設定などを使用する、みたいな場合に引っかかる流れですね

# その他
これservice作る前から立ち上がってたコンテナは起動し直さないと環境変数設定されないのですごい使いにくそうですね
namespaceまたぎもできなさそう

この先この機能が消えたり、デフォルトでオフになるとかはなさそうです
https://github.com/kubernetes/kubernetes/issues/92226
Jupyter Notebookをプレゼンにしてみた件


# はじめに

ある日突然、私はPoC開発中のものをまとめてプレゼンすることとなりました。その開発はEDAや解析がメインとなり、もちろん常識のある私はまず最初にJupyter Notebookで開発に挑みました。

Notebookはいいものの、いざプレゼンしろ！と言われましても...と思い、私は「Jupyter Notebook Presentation」という３文字をGoogleの検索バーに入れました。

なんと！感動！感動！感動！

NotebookをそのままPPTXみたいなプレゼンにできるという新世界が広がりました。皆さんもぜひ使ってみてください！

[この記事](https://qiita.com/cvusk/items/d425751ba663dc8c6517)を参考までに！（個人的にはRISEをインストールした記憶はないですが...）

# デモ
私が作成した[デモNotebook](https://raw.githubusercontent.com/YomamaBanana/Mediapipe-python/main/notebooks/test.slides.html)をそのままコピペして、[ここ](https://htmledit.squarefree.com/)でリアルタイムで表示してみましょう！

![notebook_slides.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290515/70f79465-1277-06bb-42f8-468cce73623d.gif)


# 手順

紹介した記事には詳しく記載されているので、ここで簡単にだけ手順をまとめてみます。

0. 必要であればjupyterlabをインストールする。

    ```shell
    $ pip install jupyterlab
    ```
1. notebookを作成する（ローカルでもOKAY）。
1. 動作確認する(**もしエラーを吐いてしまったらそのまま表示されちゃうので要注意！**) 。
1. jupyter notebookを起動し、次のような画面が表示されることを確認する。

    ```shell
    $ jupyter notebook path-to-notebook.ipynb
    ```

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290515/51370f8c-a7b2-2bda-c857-be9b65c6fb34.png)

1. 「スライド」「サブスライド」などを全セルに対して割りあてる。
　　　　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290515/71f3e2ba-ce60-f0c3-f254-37acbe0596bf.png)

1. 保存する。

**ここで魔法なコマンド！**

```shell
jupyter nbconvert path-to-notebook.ipynb  --to slides --post serve    
```

プレゼン形式（HTML）へ無事変換できれば自動的に表示される。

```terminal
[NbConvertApp] Converting notebook .\notebooks\test.ipynb to slides
[NbConvertApp] Writing 2161544 bytes to notebooks\test.slides.html
[NbConvertApp] Redirecting reveal.js requests to https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0
Serving your slides at http://127.0.0.1:8000/test.slides.html
Use Control-C to stop this server
```

# Dark Mode
皆さんもお気づきかと思いますが、**明るい...**
**マジで明るい...**

やっぱり私は開発といったらDark Mode人間になるので、ここで簡単NotebookプレゼンをDark modeにする方法を紹介します。

Notebookの「NbConvertApp」には便利なtheme設定がいくつか存在しており、それら上手く組み合わせれば簡単にできます。

htmlのtheme（セルと出力）

```shell
$ --HTMLExporter.theme=dark
```

slideのtheme（プレゼン背景と文字の色）

```shell
$ --SlidesExporter.reveal_theme=night
```

フルコマンド

```shell
$ jupyter nbconvert path-to-notebook.ipynb  --to slides --post serve --HTMLExporter.theme=dark --SlidesExporter.reveal_theme=night
```

### !注意!
Notebookのプロットなどは別で生成されるものなので、
matplotlibを利用する場合は「dark_background」を推奨する。

```python
plt.style.use("dark_background")
```

出力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290515/5408f216-eabe-0ee3-72cc-1fb8e4853549.png)

その他のOptionsは公式ドキュメントを参照！

https://nbconvert.readthedocs.io/en/latest/config_options.html

# あとがき
素晴らしい。
まさに開発者のため。
感動した。
pptxいらない説？！

皆さんもぜひ使ってみてください！
CI/CD戦略を考える

## はじめに
この記事は[LIGTHzアドベントカレンダー 2021](https://qiita.com/advent-calendar/2021/lightz)の8日目の記事です。
この記事では、小〜中規模のプロダクトを想定したCI/CD戦略について、自分の考えをまとめたいと思います。

## 前提条件
#### 開発チームの人数：  5人程度

#### プロダクト規模： 小〜中規模

#### 利用サービス
- Git: Azure Repos 
- CI/CD: Azure Pipelines
- Hosting: Azure App Service

## ブランチ戦略
CI/CDパイプライン構築を考える上で、**「ブランチ戦略をどうするか」**を考える必要があります。

- Git-flow
- GitHub-flow
- GitLab-flow

等いくつかあります。
各ブランチの戦略については[こちらの記事](https://qiita.com/pandama09396862/items/9f013fa7b60f4d12d1d8)でまとめてくださってますので割愛します。

個人的には、

- Gitに慣れていないメンバがいる
- デプロイの頻度が高い

といった場合はシンプルなGitHub-flowを使うとよいのではないかと思います。

## CI/CD戦略
### CI(継続的インテグレーション)
LintやUnitテスト、BuildなどはPushされたタイミングで毎回実行すると良いと思います。
CIジョブがキューに溜まってしまう場合には、CIの各処理の速度改善と併せて、並列ジョブ数を増やすことを検討をすると良いと思います。

### CD(継続的デリバリー)
デプロイ対象環境については

- 検証環境：新機能等の検証を行う環境
- ステージング環境：本番と同じ構成でリリース前の最終確認を行う環境
- 本番環境：サービス稼働環境

の3環境を想定。
3環境に対するGitHub-flowでのCD戦略は以下の表の通り

|  デプロイ環境  |  デプロイタイミング  |
| ---- | ---- |
|  検証  |  main branchへマージ  |
|  ステージング  |  main branchのコミットにtagが打たれた(*1)　|
|  本番  | 手動での承認(*2)　|

*1 tagをリリースポイントとして扱う
*2 Azurepipelineでの[approvals and checks機能](https://docs.microsoft.com/ja-jp/azure/devops/pipelines/process/approvals?view=azure-devops&tabs=check-pass)を用いることを想定。承認者はステージング環境で最終動作確認したQA担当等を想定

#### app serviceでのBlue-Green Deployment
app serviceでのホスティングの場合、ステージング・本番の各デプロイスロットを用意し、スロットをスワップすることでBlue-Green Deploymentを実現できます。
詳しくは[こちらの記事](https://blog.shibayan.jp/entry/20200428/1588009502#%E8%87%AA%E5%8B%95%E3%81%A7-Slot-%E3%81%AE-Swap-%E3%82%92%E8%A1%8C%E3%81%86)でまとめてくださっています。
(Blue-Green Deploymentって何ぞや？という方は[こちらの記事](https://tech-lab.sios.jp/archives/22822#Blue-Green)が参考になります)

本番へのデプロイ=ステージングスロットとのスワップ、といった感じです。
スワップを用いることで

- **ダウンタイムを短くできる**
- **ロールバックが容易**

といったメリットがあります。

## おわりに
今回はCI/CDの戦略について書きました。
これからもDevOpsエンジニアとして自動化を推進し、チームの開発スピード向上とシステムの安定稼働に貢献できたらと思います。
Blazor with LeaderLine

[LeaderLine](https://anseki.github.io/leader-line/)は、JavaScriptで様々な要素に矢印をひけるJavaScriptライブラリです。
今回はこのライブラリを使って、Blazorアプリケーションに彩を添えてみたいと思います:thumbsup:

**LeaderLine**

https://anseki.github.io/leader-line/

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110304/9eeeea83-54f9-1a1e-8545-1c5e02701e55.png" width=80%>

[LeaderLine](https://anseki.github.io/leader-line/)については、いくつか記事がありますので、こちらも参照してみてください。

**JSで要素同士に線や矢印を引けるライブラリ"LeaderLine"** (@RuRey0310 さん)

https://qiita.com/RuRey0310/items/ba369b46d168363f6ee2

**LeaderLineというHTML要素同士に線を引けるライブラリを使ってみる。React × TypeScript** (@sora42533 さん)

https://qiita.com/sora42533/items/5662e046c3076c812357

**[Demo](https://blazorwithleaderline.firebaseapp.com/)**

https://blazorwithleaderline.firebaseapp.com/

#使い方

LeaderLineの使い方は、とっても簡単:clap:
`id`で指定された要素同士を指定するだけで、簡単に矢印が引けます。

```html:
<div id="start">start</div>
<div id="end">end</div>
```
```javascript:
// Add new leader line from `start` to `end` (HTML/SVG element, basically).
new LeaderLine(
  document.getElementById('start'),
  document.getElementById('end')
);
```

![l2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110304/df6900a9-ee52-fa04-26c1-a31fa6cf8254.png)

#with Blazor
細かい使い方は省略いたしますが、、、
Blazorでは、どうなるかというと、ちょっと面白いことができます:point_up:
**[Demo](https://blazorwithleaderline.firebaseapp.com/)** を見ていただくと分かりますが、`Index.razor`と`NavMenu.razor`を横断して矢印をひくことができます:chart_with_upwards_trend:

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110304/6bbe5b08-87e6-f4a3-6bfe-b08cd81aac17.png" width=80%>

これはBlazorのページ構造が、SPAだからできることですね。BlazorはRazorコンポーネントで部品化しているので、`Index.razor`と`NavMenu.razor`は別々のソースファイルでありますが、Razorコンポーネント間をこんな簡単に参照できる仕組みがBlazorにはまだないので、なんだか不思議な感じですね:open_mouth:

#おわりに
LeaderLineのリファレンスページにはいろいろな矢印のひき方があり、Blazorアプリケーションに一味いろどりを添える、面白いライブラリではないでしょうか。また、実際サンプルを作ってみて、SPAであることを改めて実感することもできました:raising_hand_tone2:

今回はあまりBlazorには関係ない小ネタ感がありますが、何かのお役にたてれば幸いです:bow: