OmniAICreator/Qiita-1.07M huggingface dataset:
AWS資格取得のためにAWS初心者向けハンズオンを使ってみました

# はじめに

少し前のことですが、SOA取得に向けてハンズオンを使った学習を行ってました。
資格取得のための学習は、ほとんどの方がWEB問題集を実施されると思いますが、
実際にサービスを使ってみないと分かんないな。。
と思っている方もいらっしゃると思います。
私の実施したハンズオンをご紹介させていただきつつ、
良かった点や留意点なんかも書いてみたいと思います。

# AWS初心者向けハンズオン
私が実施したハンズオンはこちらです。AWS公式のものになります。

https://aws.amazon.com/jp/events/aws-event-resource/hands-on/

その名の通り初心者向けということで、私が学習するのにはピッタリでした。

## はじめかた
1. 上記のURLから気になるコンテンツをクリックするとハンズオンの解説画面になります。
1. 右側にイベント登録フォームが表示されますので、フォームに入力して送信することで次に進めます。
コンテンツは複数ありますが、個々にフォーム入力が必要になります。
1. 次の画面がハンズオンのページとなり、Youtubeの動画が表示されます。
動画はセクションごとに分かれてますので、１つ１つ順を追ってみていく感じになります。
1. あとは動画に従ってオペレーションしていくだけです。


## 使ってみて良かった点
- 色んなコンテンツがある
IAMの基本的な設定から始まってNetwork編やサーバレスアーキテクチャ、機械学習やAIまで様々な領域のサービスを使うことができます。
これまで使ったことのないサービスを試すのに非常に良いと思います。

- 個々のコンテンツが単独で完結している
これまで見てきたハンズオンの教材では、順を追って設定していかないと、なかなか試したいところに辿り着けないハンズオンが多く、その道のりで断念することが多かったのですが、こちらのハンズオンでは準備のところはCloudFormationテンプレートが用意されていて、試したい段階まであっさりいけました。
そのため、ハンズオン自体の時間もかからず、試したいポイントに集中することができます。
また、セクションの区切りも10分前後のものが多く、一呼吸おけるタイミングが適度にあってやりやすかったです。
- 説明が分かりやすい
AWS公式ということで、きっと説明難しいんだろうな…置いていかれてしまうんだろうな…という先入観があったのですが、初心者向けのセミナー形式のため、非常に分かりやすい説明でした。聞きやすかったです。
- 削除手順がついている
ハンズオン終わった後の削除手順がついており、消し忘れによる課金に恐れることなく安心できました。

## 留意したほうがいい点
- 試験対策にはならない
触ってみてイメージつけるところには非常に有効ですが、これで問題解けるかというとそこまでではありません。問題集での勉強はやっぱり必要です。
物足りないと思う方もいるかもしれません。その場合は、もっと高度なものにチャレンジするとか、自分なりにカスタマイズしてみるとかですね。
- 課金が発生する
EC2の起動など、どうしても課金が発生します。削除手順がついているものの、安心してハンズオンを進めるためには、以下の点のような対応をしたほうがいいと思います。
    - ハンズオン終了後、速やかに削除手順を実施する
    - Cost Explorer で使用状況を確認する
    - Budgetでコストに対するアラートを仕掛けておく
- 削除手順がないものがあった（ような…）
どれか忘れましたが（すみません）、削除手順がないものがありました。
こちらもCostExplorerで確認するなどして、課金対象で消し忘れがないか見るのがいいと思います。
- 画面がちょっと古い
2020年くらいのものもありUIが古いものがボチボチあります。
ただ、そこまで大きく変わったものはなかった印象ですので、進めるには支障ありませんでした。
- 動画なのでメモは必要
Youtubeの動画なので、内容をしっかり押さえたい場合には、自分でスクショなり撮って資料にまとめる必要があります。私はここをさぼったので復習ができませんでした。。
- ハンズオン用のアカウントを用意する
ハンズオンでは色んな操作を行うのですが、業務用のアカウントでうっかりして業務のリソース消しちゃった・・・とかないように、sandbox的用途のアカウントを用意して行うようにしましょう。
できれば個人用のアカウントがあるといいですね。
私は他の方と共用のアカウントだったので、他の方が使っていないリージョンで行ってました。

## 実施した内容
#### SOA向けに実施したもの

1. ハンズオンはじめの一歩: AWS アカウントの作り方 & IAM 基本のキ
1. Security #1 アカウント作成後すぐやるセキュリティ対策
1. スケーラブルウェブサイト構築編
1. AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する
1. Amazon EC2 Auto Scaling スケーリング基礎編
1. 監視編 サーバーのモニタリングの基本を学ぼう
1. AWS Systems Managerを使ったサーバ管理はじめの一歩編

#### 順番
1,2,3,4であとは好きなものを。という感じがいいかと思います。
1でIAM設定して、2で必要な対策をして、3でハンズオン環境の基本構成を覚えて、4でCloudformationの使い方覚えて、という進め方です。分かっているところは流し見で。

#### かかったコスト
上記7つを4日間かけて実施しました。
いくつかは動画を流し見しただけものはありますが、
完了したらすぐ削除してという感じで行ったところ、

$0.97（150円くらい）でした。

この程度なら安心ですね。

#### （おまけ）DOP向けに実施したもの
codeシリーズに関する問題がすごい多かったので…
- AWS Code サービス群を活用して、CI/CD のための構成を構築しよう！

# 最後に
まだまだAIや機械学習系など触ったことのないサービスがたくさんあるので、今後も活用していきたいと思います！
ありがとうございました。


<br>
<br>

## AWSの相談・お困りごとありましたら、、、
AWSの活用方法や、お困りごとの相談、随時、お仕事の受付しております。

https://www.cs-cloud-aid.com/
シェルスクリプトでCRONジョブを自動追加

ユーザーは定期的に実行したいスクリプトやプログラムをCRONジョブとして設定できます。しかし、既に同じジョブが設定されていないかを確認し、重複を避けながら新たなジョブを追加する自動化スクリプトの作成は少々複雑になり得ます。

### 必要条件
- Linux OS
- シェルアクセス

### スクリプトサンプル

```bash
#!/bin/bash

# ジョブ定義
job="0 5 * * * /path/to/your/script.sh"

# crontabにジョブがすでに存在するか確認
crontab -l | grep -Fq "$job"

# 確認の結果、ジョブが存在しない場合は追加
if [ $? -ne 0 ]; then
    (crontab -l; echo "$job") | crontab -
    echo "CRON job 追加した"
else
    echo "CRON job 既に存在する"
fi
```

- ジョブ定義: スクリプトやコマンドを含むジョブの全体的な設定を行います。
- ジョブ存在確認: crontab -l で現在のCRONジョブリストを取得し、grep で特定のジョブがリスト内に存在するか確認します。
- 条件分岐: ジョブが存在しない場合、既存のCRONジョブに新しいジョブを追加しています。
- `(crontab -l; echo "$job") | crontab -`：crontabリスト (`crontab -l`)及び新JOB(`echo "$job"`)はcronに書き込み(`crontab -`)。　


### 参考
- `grep -F` : 指定した各パターンを、正規表現ではなく文字列として扱います。
- `grep -q` : 行の一致に関係なく、標準出力への書き出しをすべて抑止します。 

https://www.ibm.com/docs/ja/aix/7.3?topic=g-grep-command
[Godot 4.2] Button の上に Container を使ってサブの Button を置く

例として、ターン制ゲームの技選択画面を作ろうとしたときのことを考えます。
大抵の場合は各技の名前が書かれたボタンを並べると思います。
このとき、技のボタンの端っこなどにその技の詳しい解説を表示するボタン、あるいはもっと別の機能を持ったボタンを付けたい場合、この記事が役立ちます。


# 全体図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699119/a4b24b0e-6dc1-cbac-c1bc-687f86ed4c71.png)

SubButton をクリックすると MainButton は反応せず、それ以外の場所("Title XXX" と表示されている箇所)をクリックすると MainButton が反応します。

# ポイント
## PanelContainer を使う
[PanelContainer](https://docs.godotengine.org/ja/4.x/classes/class_panelcontainer.html) は数ある Container の一種で、子要素を自身の内側いっぱいに広げて配置します。
これに複数の子があった場合、重なりを無視してすべての子を広げます。

ちなみに、本来は任意のUI要素に枠を付けるために使われるようです。

## `mouse_filter` を操作する

目的に合わせて [`Control.mouse_filter`](https://docs.godotengine.org/ja/4.x/classes/class_control.html#class-control-property-mouse-filter) を変更します。
このプロパティの詳しい解説は [[Godot 4.2] 3種のMouseFilterの挙動解説 #tips - Qiita](https://qiita.com/aka3/items/2041898f9e558e7a8b1f) に書いてあります。

今回は `MainButton` と `SubButton` を `MOUSE_FILTER_STOP` にして、それ以外を `MOUSE_FILTER_IGNORE` に設定しておきます。


# 参考文献
[Godot EngineでContainerにButton機能を持たせる](https://zenn.dev/ankouh/articles/b6490ec80d6cba)
[PanelContainer — Godot Engine (4.x)の日本語のドキュメント](https://docs.godotengine.org/ja/4.x/classes/class_panelcontainer.html)
[Control — Godot Engine (4.x)の日本語のドキュメント](https://docs.godotengine.org/ja/4.x/classes/class_control.html#class-control)
【Python】unittestモジュールとparameterizedライブラリを使用して効率的にテストケースを書く

# 概要
Pythonの`unittest`モジュールと`parameterized`ライブラリを使用すると、さまざまな組み合わせのテストを簡潔に記述できることがわかったので紹介します。似たようなテストをしたい場合は、ダラダラとテストケースを羅列するとなかなか面倒であり、また保守性も低いです。そんな時に`parameterized`ライブラリは使いやすいと思います。


https://pypi.org/project/parameterized/


# サンプルコード

```py
import unittest
from parameterized import parameterized

class TestFruits(unittest.TestCase):

    FRUIT_COLORS = {
        "apple": "red",
        "banana": "yellow",
        "grape": "purple",
    }

    @parameterized.expand([
        ("apple", "red"),
        ("banana", "yellow"),
        ("grape", "purple"),
    ])
    def test_fruit_color(self, fruit, expected_color):
        self.assertEqual(self.FRUIT_COLORS[fruit], expected_color)
```

上記`test_fruit_color`テストケースでは、各フルーツの色が期待される色と一致することを確認しています。(`FRUIT_COLORS`辞書から取得したフルーツの色と、テストケースのパラメータとして提供された色を比較)

`apple`の場合は`red`であるかどうか、といった形です。

`parameterized.expand`デコレータを使用することで、テストケースを各フルーツと色の組み合わせに対して実行できるのです。

`test_fruit_color(self, fruit, expected_color)`では、`fruit`には`parameterized.expand`デコレータによって提供されるパラメータである`"apple"、"banana"、"grape"`が入り、`expected_color`の方には、`"red"、"yellow"、"purple"`が渡されます。

ちなみに上記では引数は二つでしたが、一つや三つでも可能です。

```py
import unittest
from parameterized import parameterized

class TestFruits(unittest.TestCase):

    FRUIT_COLORS = {
        "apple": "red",
        "banana": "yellow",
        "grape": "purple",
    }

    FRUIT_PRICES = {
        "apple": 100,
        "banana": 50,
        "grape": 200,
    }

    # 引数一つの場合（辞書に含まれているかどうかの確認）
    @parameterized.expand([
        ("apple",),
        ("banana",),
        ("grape",),
    ])
    def test_fruit_exists(self, fruit):
        self.assertIn(fruit, self.FRUIT_COLORS)

    # 引数二つの場合（組み合わせが正しいかどうかの確認）
    @parameterized.expand([
        ("apple", "red"),
        ("banana", "yellow"),
        ("grape", "purple"),
    ])
    def test_fruit_color(self, fruit, expected_color):
        self.assertEqual(self.FRUIT_COLORS[fruit], expected_color)

    # 引数三つの場合（組み合わせが正しいかどうかの確認）
    @parameterized.expand([
        ("apple", "red", 100),
        ("banana", "yellow", 50),
        ("grape", "purple", 200),
    ])
    def test_fruit_color_and_price(self, fruit, expected_color, expected_price):
        self.assertEqual(self.FRUIT_COLORS[fruit], expected_color)
        self.assertEqual(self.FRUIT_PRICES[fruit], expected_price)

if __name__ == '__main__':
    unittest.main()
```

`@parameterized.expand`に渡すリストの各要素と、テストメソッドの引数の数と一致していない場合は失敗します。順序も同じになります。

ただし、`test_fruit_color`メソッドは2つの引数を期待しているにも関わらず、`@parameterized.expand`デコレータで提供されているテストケースは1つの引数しか提供していない場合、このテストケースは「スキップ」されます。失敗するわけではないので要注意です。
`test_fruit_color`メソッドの引数の方が少ない場合は`TypeError`なります。


ここでの例では、`@parameterized.expand`のリスト内に直接データを記述していますが、実際に運用する場合は、別の関数の結果を渡してあげたり、DBからデータを取得して渡してあげたりすると管理しやすいかと思います。
配列内のオブジェクト、任意の値を比較してランキングを作成（練習）

「配列内のオブジェクト」ということなので、以下のようなコードで実験用の配列を作成していきます。

~~~js
/*---配列内にオブジェクトを収めるサンプルコード ---*/

const arr = [...Array(10)].map((_, i) => {
  return {
	  index: i + 1,
	  number: (Math.random() * 100),
	}
})

console.log(arr);
~~~
~~~js
  //　結果
[{
  index: 1,
  number: 91.25240273034892
}, {
  index: 2,
  number: 50.30337793565278
}, {
  index: 3,
  number: 56.7219849866174
}, {
  index: 4,
  number: 78.14357103773145
}, {
  index: 5,
  number: 56.00559517743395
}, {
  index: 6,
  number: 83.7587424204647
}, {
  index: 7,
  number: 97.54322322663951
}, {
  index: 8,
  number: 96.49660404863312
}, {
  index: 9,
  number: 24.422683577051842
}, {
  index: 10,
  number: 78.23240765349418
}]
~~~

- 要素数10の配列、要素にオブジェクト
- 「number」の値はrandom()なので実行毎に数値が変わる
- map()のreturnをオブジェクトに出来ることが地味にビックリ

## やりたい事
- 各「number」の値を比較して、ランキングを作成したい
- sort()系を使い、値の比較と並び替え
- 配列要素を順番に取り出し、ベストやワーストを出す

そして、作成したのが以下のコードになります。

~~~js
/*---ランキングを作るコード ---*/

const ranking = (arr, key, n) => {  
	const arr2 = arr.toSorted((a, b) => (b[key] - a[key]) * Math.sign(n));
    const order = [...Array(Math.abs(n)).keys()]
	    .map(e => arr2.at(e));
				
	order.unshift({
	      index: 0, 
		  title:`${n > 0 ? 'ベスト': 'ワースト'}${Math.abs(n)}`,
    });
    return order;
}

const bestRank = ranking(arr, 'number', 3);
const worstRank = ranking(arr, 'number', -3);
~~~

~~~js
console.log(bestRank);

  // 結果
[{
  index: 0,
  title: "ベスト3"
}, {
  index: 7,
  number: 97.54322322663951
}, {
  index: 8,
  number: 96.49660404863312
}, {
  index: 1,
  number: 91.25240273034892
}]
~~~
~~~js
console.log(worstRank);

 // 結果
[{
  index: 0,
  title: "ワースト3"
}, {
  index: 9,
  number: 24.422683577051842
}, {
  index: 2,
  number: 50.30337793565278
}, {
  index: 5,
  number: 56.00559517743395
}]
~~~

### .toSorted()
通常、sort()で昇順、降順をさせようと思った時、
- sort((a, b) => a - b)　昇順
- sort((a, b) => b - a)　降順

と紹介されている事が多いのですが、コード的に aとb をひっくり返すのが何故か **面倒くさい** と思ってしまいました。
とりあえず、b-a に1か-1を掛け算したら、上手く昇順と降順が表現できたので、一先ずこれでOKとしました。
尚、**toSorted()は、sort()の非破壊的版** らしいです。積極的に使っていきたいです。

### .at()
配列のインデックス番号を指定して読み込みに行くメソッドです。
今までのarr2[i]を使っても良かったのですが、arr2.at(i)がカッコ良さそうな感じがして、なんとなく使って見ました(笑)

今回の昇順降順に関しては、arr2.at(i) で i の値を
- 0,1,2 
- -1,-2,-3

とすることでランキングのベストとワーストを出せたハズで、そういったコードも作る事は出来たと思うのですが、今回は .toSorted() に任せる事としました。

### .find()、最大値
次に、ランキングではないのですが、最大値を持つオブジェクトも出してみようと思います。

~~~js
/*--- 最大値 を検索、.find()で抽出するコード ---*/

const maxNum = arr.reduce((a, c) => Math.max(a, c.number), 0);
const maxObj = arr.find(e => e.number == maxNum);

console.log(maxObj);
~~~
~~~js
 // 結果
{
  index: 7,
  number: 97.54322322663951
}
~~~

### .filter()、～未満
ついでに、～未満 も抽出してみます。

~~~js
/*--- ～未満 を.filter()で抽出するコード ---*/
const belowObj = arr.filter(e => e.number < 55);
console.log(belowObj);
~~~
~~~js
  // 結果
[{
  index: 2,
  number: 50.30337793565278
}, {
  index: 9,
  number: 24.422683577051842
}]
~~~

比較条件を別に算出している場合は、.find() や .filter() を使い元のarrから該当要素を抽出する事も出来ますね。

## まとめ
- オブジェクトの値を比較して、**配列内のオブジェクトを直接ソート** が可能
- オブジェクトの値で .find() や .filter() をすると、**オブジェクト自体の抽出** が可能

という事で、
- もっと良い方法があるよ
- もっと簡潔に書けるよ
- それ、まちがってるよ

などありましたら、コメントでお待ちしています。
### 参考記事

https://qiita.com/ndj/items/82e9c5a4518fe16e539f
esp32 c3系でシリアルモニタに出力されない

## esp32 c3系にてSerial.printしてもモニタに出力できない
arduino2.0になり、しばらく更新せずに久しぶりにC3系のマイコンに書き込んでみるとSerial.printの結果がモニタには出ておらず、しかしLEDなどの挙動を見る限り書き込めてはいそうという現象が発生。

### 解決した方法
arduino IDEのメニューにあるツールをクリック
![スクリーンショット 2024-04-25 18.45.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1055872/df95e294-5218-17c7-b394-981df0d00750.png)
ツール内の
USB CDC On Boot:
という項目が有効になっていないとC3系はシリアルモニタに出力されないことがあるらしい。
この項目にホバーするとDsabled/Enabledと切り替えれられるのでEnableｄを選択

今までは特にarduino IDEの設定を操作せずに書き込んでも出力できていた気がしたがなぜかこのタイミングで発生

この設定で書き込むとシリアルモニタに出力できるようになった
arduinoのタブを複数起動している場合は他のタブにこの設定は反映されないため注意

他に試したことはSerial.begin()のスピードを9600や115200に変えてみたり、アップロードスピードを変えてみたりだが、同じようなトラブルに見舞われている人の参考になれば嬉しいです
【Laravel】FormRequestでファイル名をバリデーション

# はじめに
LaravelのFormRequestではファイル形式のバリデーションもサポートしていますが、**ファイル名**でバリデーションしたい場合は、カスタムルールを作成する必要があります。

本記事では、カスタムルール`Filename`を作成し、ファイル名の**長さ**と**正規表現チェック**を実装します。

# Filename実装

Rules\Filenameクラス作成
```terminal
php artisan make:rule Filename
```

実装内容

```php:Filename.php
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class Filename implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $name = $value->getClientOriginalName();

        // 長さ
        $nameLengthMax = 100;
        $nameLength = mb_strlen($name);
        if ($nameLength > $nameLengthMax) {
            $fail(":attributeのファイル名は拡張子を含めて{$nameLengthMax}字以内にしてください。");
        }

        // 正規表現
        $pattern = "/[^a-zA-Z0-9ぁ-んァ-ヴー一-龠!_.*'()\-]/u"; // 絵文字などは引っかかる
        if (preg_match_all($pattern, $name, $matches)) {
            $invalidCharacters = $matches[0];
            $fail(":attributeのファイル名に不正な文字（" . implode(', ', $invalidCharacters) . "）が含まれています。");
        }
    }
}
```

`Filename`では、ファイル名の**長さ**と**正規表現**を確認しています。正規表現については、引っかかったものをエラーメッセージで全て列挙するようにしています。
使用する場合は、お好みに応じて調整頂ければと思います。

# 使用例

使い方は以下になります。

```php:SampleRequest.php
use App\Rules\Filename;

class SampleRequest extends FormRequest
{

    // 〜省略〜

    public function rules(): array
    {
        return [
            'sample_file' => ['nullable', 'mimetypes:image/jpeg,image/png', 'max:1024', new Filename()],
        ];
    }
```

# まとめ

デフォルトで多くのルールを網羅しているFormRequestですが、細かいことをするにはカスタムルールを作成する必要があります。
カスタムルールを使えば、複雑な条件にも対応できるので、ぜひ使っていきましょう。

# 告知
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。

https://eda-inc.jp/recruit/

みなさまからのご応募をお待ちしております。
実践！オフショア・アジャイル～ベトナムとつながるスクラムの旅

Xin chào! :flag_vn: こんにちは！:flag_jp:
ポーラ・オルビスホールディングスのITプロダクト開発チームで、スクラムマスターを務めている川田です。

以前に別の記事でご紹介したように、私たちはベトナムのオフショアチームと一緒に開発を進めています。（冒頭のXin chàoはベトナム語でこんにちは、の意味です:sunglasses:）

https://qiita.com/hayakawa_POHD/items/db2f9c7c72725132644c

言語・文化の違いや時差がある中で、オフショア開発を実践してみて大事だなと感じたポイントや工夫していることをご紹介します！

# オフショアとスクラム開発
スクラム開発を進めるうえで困ったことがあったとき、私は「スクラム現場ガイド」という書籍をよく参照します。（マイナビ出版、Mitch Lacey著、安井力・近藤寛喜・原田騎郎 訳、ISBN：978-4-8399-5199-3）

https://book.mynavi.jp/ec/products/detail/id=50668

こちらの書籍の「第4部 上級サバイバルテクニック」の「28章 アウトソースとオフショア」では、以下のように記されています。

> オフショアへのアウトソースは、どんな手法でも難しいものだ。とりわけアジャイルなプラクティス、たとえばペアプログラミングやコアタイム、デイリースタンドアップを考えれば、アジャイルとアウトソースの組み合わせは込み入ったものとなるし、ある意味で高価に付く。

この書籍は2016年に発売されたものですが、コロナ渦を経て世の中の状況は大きく変わりました。リモートで業務を進めるための技術・サービス・プロセスが整備され、以前よりはオフショア開発のハードルが下がっているのではないでしょうか。それでも、オフショアチームと一緒にスクラムを組み、アジリティ高くプロダクトをリリースするためには工夫が必要です:rugby_football::rugby_football::rugby_football:

# 適切なアウトソース先（国）を選択する
そもそも論として、アジャイル開発に適したアウトソース先を選択することが重要です。システム運用をアウトソースする場合であれば、あえて時差の大きな国を選ぶことでお互いが夜間になる時間帯をカバーし合うことができます。一方で、ソフトウェア開発においては時差が少ないほどコミュニケーションの効率がよいです。ちょっとした質問の回答や作業の依頼を行うのに、時差によって1日かかってしまうのはもったいないですよね。日本におけるソフトウェア開発のアウトソース先としてベトナム・フィリピン・インドの人気が高い理由の1つはここにあると考えています。

# タスクの振り分け
ウォーターフォール開発であれば、実装フェーズやQAをまとめてアウトソースする手段が取れると思います。アジャイル開発ではプログラマーやテスターといった限定したロールのメンバーを置くことは基本的にしないので、一貫した機能（ユーザーストーリー）をどのように振り分けるかを考える必要があります。私たちのチームでは、特に以下のポイントを考慮してタスクの振り分けを行っています。
- 期限の長さ（緊急度）
- 要件や実現方法が明確かどうか

スクラムにおいてはWIP（Work In Progress、仕掛中のタスク）が無くなった時点でスプリントバックログの上から順番にタスクを取れば、振り分けは必要ないのではと思われるかもしれません。しかしながらタスクの性質に応じてざっくり振り分け、オンショアとオフショアそれぞれのチームで自立的にタスクを進められる方が全体としてアジリティは高くなります。スクラムチームが2つあって、共通のバックログとコードベースを用いて協調しながら業務を進めるイメージですが、片方のチームがもう一方のサブチームとなるのではなく、対等な関係性の自立したチームとして体制を整えることができるならアジャイル開発においてオフショアを活用できる可能性は十分にあると思います:+1:

# 必ず実施するスクラムイベント
スプリントの中で実施するスクラムイベントはオンショアとオフショアの両チームで一緒に実施することが理想ですが、言語の違いもあり、出席するよりは開発を進めた方が良いのではと感じることもあるでしょう。スクラムイベントに出席するだけが目的になっているのであればやめるべきですが、チーム毎にイベントを開催することで言語を気にすることなく効率的に機能する可能性が高いため、省略せずに必ず実施した方が良いものがあります。それがデイリースクラムとスプリントレトロスペクティブです。
デイリースクラムはスプリントゴールに対する検査と適応を行う場なので重要であることは明白ですが、オフショア開発を活用するうえでは開発プロセスに対する検査と適応を行う場であるスプリントレトロスペクティブが特に重要であると感じています。オンショアとオフショアそれぞれのチーム内でのプロセスはもちろんのこと、チーム間のコミュニケーションとコラボレーションにも着目し、双方の視点で検査します。また、振り返った内容をお互いに共有することでチームの状況、考え方、文化を理解・尊重し、同じプロダクトを作るうえでベストな開発プロセスを一緒に考えていくことが重要です:two_men_holding_hands::two_women_holding_hands:

# ブリッジSEの重要性
スクラムイベントを進める上で、そして日々の細かなコミュニケーションにおいても、オフショア開発におけるブリッジSEの役割は非常に重要です。オンショアとオフショアチーム間で共通言語を用いてコミュニケーションが取れたとしても、異なる考え方や文化、細かいニュアンスの違いをうまく橋渡しする調整役が必要です。ブリッジSEを通してオンショアとオフショアが一体となり、ワンチームになれるかどうかがアジャイル開発におけるオフショア活用の成功のカギ:key:であると感じます。
また、ブリッジSEに求められる能力はアジャイル開発だからといって特別な内容はありません。アジャイル開発の特性を活かし、個人の対話や顧客との協調を優れたコミュニケーション能力で支えること、そして変化へ迅速に対応する柔軟性と適応力が特に重要です。

# 最後に
最初から開発体制を整えることが難しく、オフショア開発の利用を検討するケースは少なくないと思います。昨今で人気のアウトソース先は国をあげてITに力を入れているためエンジニアのレベルも高く、アジャイル開発の経験があるメンバーも増えています。
私たちの取り組みが皆さんのオフショア活用の参考になれば幸いです！:smiley:
オブジェクトロック情報の一覧表示

物理ファイル(テーブル)や画面ファイルの更新/コンパイルを行おうとして、別のユーザや実行中のJOBにロックされていて **おんぎゃぁぁ** :sob: となる事はありませんか？
私はよくあります。

誰だロックしているヤツはとぼやきながら、このSQLを実行しましょう
直ぐに ~~犯人~~ ロックしているJOB がわかります。

## 用意する物
IBM i 
IBM i AccessClientSolutions (ACS) の SQLスクリプトの実行
もしくは 5250端末で STRSQL

## オブジェクトロック情報一覧を取得

```
WITH LOCK_CONFLICT_TABLE (object_name, lock_state, q_job_name) AS (
SELECT object_name, lock_state, job_name
FROM QSYS2.OBJECT_LOCK_INFO 
where 
 object_schema = 'ライブラリ/スキーマ名' 
 and object_name LIKE 'オブジェクト名'
) 

SELECT OBJECT_NAME
, LOCK_STATE
, Q_JOB_NAME
, V_SBS_NAME
, V_JOB_STATUS
, V_ACTIVE_JOB_STATUS
, V_ACTIVE_JOB_TYPE
FROM LOCK_CONFLICT_TABLE
, TABLE(QSYS2.GET_JOB_INFO(q_job_name)) B;
```
LaravelでFirebase認証を使う

# 記事の内容
LaravelでFirebase認証を実装時にちょっと詰まったので、未来の自分が困らない＆誰かのだめになればと記録に残しておきます。

## 事前準備
Composerでkreait/firebase-phpとkreait/laravel-firebaseをインストールする。

```
composer require kreait/firebase-php
composer require kreait/laravel-firebase
```

.envにFirebaseのクレデンシャル情報を記載したjsonファイルのパスを記述する。
※jsonファイルはfirebaseからダウンロードしてください。

```
FIREBASE_CREDENTIALS=firebase_credentials.json
```

config配下に設定ファイルを作成。

```
php artisan vendor:publish --provider="Kreait\Laravel\Firebase\ServiceProvider" --tag=config
```

config/firebase.phpができます。
特に編集はしなくてOKです。

## コントローラーの記述
事前準備ができたらartisanコマンドでコントローラーを作成してください。
以下はコントローラーの記述です。

``` FirebaseController.php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Kreait\Firebase\Contract\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\JsonResponse;

class FirebaseController extends Controller
{
    /**
     * @var Auth
     */
    private $auth;

    /**
     * コンストラクタで認証情報を用意する
     * @param Auth $auth
     */
    public function __construct(Auth $auth)
    {
        $this->auth = $auth;
    }

    /**
     * 認証
     * @param  Request  $request
     * @return JsonResponse
     */
    public function auth(Request $request): JsonResponse
    {
        $email = $request->input('email');
        $password = $request->input('password');

        try {
            // メールアドレスとパスワードでFirebaseにサインインする
            $firebaseSignInResult = $this->auth->signInWithEmailAndPassword($email, $password);

            $idToken = $firebaseSignInResult->idToken(); // IDトークン
            $expiresIn = $firebaseSignInResult->ttl(); // トークン有効期限
            $userToken = $firebaseSignInResult->firebaseUserId(); // ユーザーID

            // あとはIDトークンなどを使用した処理を記述する
        } catch (\Exception $e) {
            Log::error($e->getMessage());
        }
    }
}
```

あとはtry~catchの部分をいじって`$firebaseSignInResult`の有無で処理を分岐させれば簡易的なログイン機能が実装できるかと思います！
14日目「if」と「try-catch」で揺れる社畜L

## 🔍はじめに  
「paiza」の一問題について、  関数を作成したい。

しかし、関数内の"条件文"が複雑になってしまう  
そこで、try-catchを用いることで、  
すっきりとしたコードになるのではないかと考え検討した。

## 🔎本題のコード(JavaScript)

- Sample1(if文)
```js
let evol=(num)=>{
    //条件文
  if(botinfo[num-1][0]<H && 0<=botinfo[num-1][0] && botinfo[num-1][1]<W &&0<=botinfo[num-1][1]){
    //メイン処理
    if(toolbox[botinfo[num-1][0]][botinfo[num-1][1]]==="T"){
      if(botinfo[num-1][2]!=4){
        botinfo[num-1][2]++;
      }
    }
  }
}
```
- Sample2(try-catch文)
```js
let evol=(num)=>{
    //メイン処理
  try{
    if(toolbox[botinfo[num-1][0]][botinfo[num-1][1]]==="T"){
      if(botinfo[num-1][2]!=4){
        botinfo[num-1][2]++;
      }
    }
  }catch(TypeError){

  }
}
```
## 🔍２つのコードの比較結果
少ない実行回数では大きな差は出なかったが、10万リクエストしたら下記のように結果がでた！
|コード|実行時間[sec]|備考|
|-|-|-|
|sample1（if文）|4.23||
|sample2（try-catch文）|4.33|あれれーー？おっかしいぞーー!?|
  
try-catch文は、メイン処理を試行して例外が生じてからスキップするのに対し、  
if文は、条件外ならそもそもメイン処理の試行すら行わないため、  
ムダな処理をしなくなった分速くなったと考えられる。  

またtry-catch文で用いた`TypeError`は  
"変数やオブジェクトの型が合わないときに発生するエラーすべて"  
を拾ってしまうため、無視してよい条件だけでなく、  
本来無視できない状態でも後続処理に通してしまうリスクが考えられる。  
（バグの埋め込みになる）

## 👓真実はいつも１つ！（結論）
その時の要件/仕様にもよると思うが、今回のケースのように**条件が明らかな場合**は、  
**条件文を明示しておくことでメイン処理の前に分別をつけておく**方が、  
パフォーマンス、保守性の観点からみても好ましいのではないかと考えた。
【Laravel】XSS / CSRF / SQLインザラクション

## XSS / CSRF / SQLインザラクション
https://qiita.com/4649rixxxz/items/d31ecb33fcba1bffcb90

## 3分でわかるXSSとCSRFの違い
https://qiita.com/wanko5296/items/142b5b82485b0196a2da
AWS Web Application Firewall (AWS WAF) を使用して悪質なトラフィックをブロックする

ハンズオントレーニングで、WAFの設定を確認しました。今回の内容を見て、やれそうだと思った方は、ハンズオンサイトも参考にして、試してみてください。

参考にしたサイト
https://catalog.workshops.aws/startup-security-stay-secure-while-building-quickly/ja-JP

以前作成した AWS WAFとAWS Shieldの違い
https://qiita.com/kimuni-i/items/3c155761f0509b2dbcba

WAFの作成手順
--
### 1.ルールの作成

### 2.ルールの設定

1. **ルールセットの選択**
AWS が提供する管理ルールセットを使用するか、独自のルールを作成します。

2. **ルールの追加**
選択したルールセットに基づいて、ブロックしたい条件を指定します。
ルールの追加ボタンをクリックし、ブロックしたいリクエストパターンや許可したいリクエストパターンを指定します。
例えば、XSS フィルタリングや SQL インジェクション対策などのルールを追加できます。

### 3.Web ACL の関連付け
 Web ACL をアプリケーションに関連付けます。
関連付けたいアプリケーションのリソース（例: CloudFront ディストリビューション、Application Load Balancer）を選択し、Web ACL を関連付けます。

具体的な手順
--
WAFの名前を決めます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/91b13394-3b8c-537c-68a5-7f26403ed509.png)

次に、どんなリソースを保護するのか決めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/89a4cbbe-c6d6-48f0-64ee-909dd8feef36.png)

マネージドルールを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/49aee0d1-a3ce-f45b-d980-468f9117be9e.png)

AWS managed rule groups の下を下にスクロールすると SQL database という名前が表示されます。右側の Add to web ACL トグルをクリックして、そのルールグループを有効にします。
![21.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/2483e95d-e408-77d7-ee59-32d658146932.png)

カスタムルールを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/f7d1df8f-71a4-8bae-dc1a-77bccf53d919.png)

![22.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/5baa9eaf-fdc8-aefc-cfd6-169f3b84e3af.png)

![23.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/05e8676a-74db-631d-8d44-3c614a5471b1.png)


**Inspect: Body** 
リクエストボディを検査します。つまり、リクエストの本文部分をチェックします。

**Content Type**
Plain text 
リクエストボディのコンテンツタイプがプレーンテキストであることを指定します。つまり、リクエストボディがテキストであることを確認します。

**Match type**
Contains string
リクエストボディが指定した文字列 "global.process" を含む場合にマッチするように設定します。

**Text transformation**
None
文字列の変換を行いません。つまり、マッチングは大文字小文字を区別します。

**Oversize handling**
Continue
サイズの超過したリクエストに対して、このルールを続行します。つまり、サイズの大きなリクエストに対してもこのルールが適用されることを許可します。

この設定により、リクエストのボディ部分に "global.process" という文字列が含まれる場合、そのリクエストはブロックされます。これにより、JavaScript がサーバーサイドプロセスにアクセスする試みが阻止されます。

Review and create web ACL の画面で、すべての情報が正しいことを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/9191cbe5-04cc-7b97-6803-ca7238cf6070.png)

この設定をもとに、ハンズオンを実施すると、次のような結果を確認する事が出来ます。
https://catalog.workshops.aws/startup-security-stay-secure-while-building-quickly/ja-JP/90-retry-attack
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/f63f8c75-c855-6f82-ad1a-8f2d5fac3b69.png)

コマンドレベルで、自分で攻撃を行ってみる事が出来るので、勉強になります。
Zoom の「手を挙げる」を他のアプリを使用中にも操作したい

# Zoom の「手を挙げる」を他のアプリを使用中にも操作したい

会議で「手を挙げる」場合に，他の作業をしている場合，一旦 zoom アプリをActiveにしたあとに，リアクションから「手を挙げる」を選ぶか，キーボードショートカットの Alt + y を押しているかと思います．

結構時間がかかるので，AutoHotKey で Alt + Shift + y を押すとなにか作業中でも Zoom で手を上げることができるスクリプトを書いてみました．

実装は単純で，
1. Zoom ミーティング という Windows を見つける
2. その Windows を Active にする
3. Zoom に Alt + y を送る

としているだけです．

```
<!<+y::
   IfWinExist, Zoom ミーティング
   {
     WinActivate
     Send, !y
     return
   }
```
List.Contains と HashSet.Containsってどのくらい性能差があるのか検証してみた

## はじめに

とあるプロジェクトにて、とあるデータのコレクションをListで扱っていたのですが、
今後の性能を考えHashSetとして扱うか・・・コレクション順序をどうするか・・・
など設計を再検討する機会がありました。

実装上Containsを多用していたため、
List.Contains と HashSet.Containsってどのくらい性能差があるのか
を実際に検証してみることにしました。

前提として、メカニズムには以下の違いがあり、HashSetが高速です。
> ListとHashSetのcontainsメソッドの速度差は、要素数によって異なりますが、一般的にはHashSetの方が高速です。
> これは、HashSetがハッシュテーブルを使用しており、要素の検索に平均O(1)の時間がかかる一方で、Listの場合は要素を順番に比較する必要があり、平均O(n)の時間がかかるためです。


## 検証コード

※コレクション要素を全検索します。

```CSharp

Console.WriteLine("please enter the int number");
var count = int.Parse(Console.ReadLine());

var userList = CreateUserList(count);
Measure(() =>
{
    foreach (var user in userList)
    {
        userList.Contains(user);
    }
}, "List");

var userHash = CreateUserHash(count);
Measure(() =>
{
    foreach (var user in userHash)
    {
        userHash.Contains(user);
    }
}, "HaseSet");

return;

void Measure(Action action, string type)
{
    var sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    Console.WriteLine($"{type}:{sw.ElapsedMilliseconds}msec");
}

IList<User> CreateUserList(int count)
{
    var users = new List<User>();
    for (var i = 1; i <= count; i++)
    {
        users.Add(new User(i));
    }
    return users;
}

HashSet<User> CreateUserHash(int count)
{
    var users = new HashSet<User>();
    for (var i = 1; i <= count; i++)
    {
        users.Add(new User(i));
    }
    return users;
}

class User
{
    public int Id { get; set; }

    public User(int id)
    {
        Id = id;
    }
}

```

## 結果

```
please enter the int number
1000
List:2msec
HaseSet:0msec
```

```
please enter the int number
10000
List:81msec
HaseSet:1msec
```

```
please enter the int number
100000
List:2816msec
HaseSet:5msec
```


## GitHub

https://github.com/i-tanaka730/ContailsPerformance
GASのS3ライブラリで署名バージョン4の認証エラーが出た時の解決法

# はじめに

まず、GASとS3を連携したいと思った時に以下のS3ライブラリが挙げられると思います。

https://engetc.com/projects/amazon-s3-api-binding-for-google-apps-script/

https://qiita.com/GoeMon564/items/851b76a5c78b22a64fe2

ただ、こちらをそのまま利用しようとすると署名バージョン4に対応しておらず、困る場合があるかと思います。

https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html

>Amazon S3 は、すべての AWS リージョンで、AWS サービスへの受信 API リクエストを認証するためのプロトコルである署名バージョン 4 をサポートしています。現時点では、2014 年 1 月 30 日より前に作成された AWS リージョンは、以前のプロトコルである署名バージョン 2 を引き続きサポートします。2014 年 1 月 30 日以降の新しいリージョンは署名バージョン 4 のみをサポートするため、これらのリージョンへのすべてのリクエストは署名を使用して行う必要があります。

とあるように、新しく作成されたリージョンでは署名バージョン4のみをサポートするため、例えば`ap-northeast-1(東京)`リージョンでは署名バージョン4の認証エラーは発生しませんが、`ap-northeast-3(大阪)`リージョンでこのライブラリをそのまま利用しようとした場合エラーが発生します。

この記事では、この問題を解決してGASから署名バージョン4のS3と連携ができるようにする方法を書いていきます。

# 方法

GAS上のS3ライブラリは署名バージョン4に対応していないですが、github上では対応がされているので、githubからS3ライブラリのコードをGASのファイルへコピーしてください。

コピーする対象は`S3.gs`と`S3Request.gs`の2つです。

https://github.com/eschultink/S3-for-Google-Apps-Script

あとは、`S3Request.gs`のregion指定を実際に使用するリージョンに書き換えてください。

```javascript
this.date = new Date();
this.serviceName = 's3';
this.region = 'us-east-1'; // ←ここ
this.expiresHeader = 'presigned-expires';
this.extQueryString = '';
```

>https://github.com/eschultink/S3-for-Google-Apps-Script/blob/7e4b2f963d0fb3533eda2f55b8013acad8055d76/S3Request.gs#L22-L26

以上です。

# おわりに
誰かのお役に立てれば幸いです。
歴4年のエンジニアがiOSアプリ＋WordPress等の技術ブログ作ってみた！

# ご挨拶
みなさんはじめまして！
エンジニア歴4年の高澤と申します！

CMSでのサイト構築やWebデザイン制作を兼任して4年目（2024年4月時点）になります！
たまに動画編集もします！

主にWordPressエンジニアとしてテーマ開発を行ったり、管理画面のカスタマイズやプラグインのカスタマイズなどをおこなってウェブサイト制作のエンジニアを担当してきました。

自作のWordPressプラグインを開発することもあり、WordPressには大変お世話になっております！

WordPress以外にも、MovableTypeやCMS Hub、またECプラットフォーム領域では、Shopify、ECCUBE4、MakeShop、楽天などのコーディングとWebデザインの納品実績があります。

最近はスマホアプリ開発に興味があり、最近ではSwift UIでiOSアプリを自主開発し現在Apple Storeに公開しております。

↓こちらが高澤が開発したiPhoneアプリになります！

https://concafewatch.com/

↓また、こちらは僕のお仕事の実績ご紹介ページになります。

https://wordpress-mag.com/author/

よろしくお願いいたします！

# 思い切って技術ブログを公開

エンジニア歴4年にして、自分で技術ブログを立ち上げました！
ブログ名は「WordPress MAGAZINE ワードプレスマガジン」です！

サイト名含めてだいぶ思い切りました笑

内容としては主にCMS、特にWordPress構築の技術解説ブログになりますが、他の技術、特にスマホアプリ開発などにも興味があるので、幅広くIT技術についてこのブログで解説していければと思っております！

もちろん当サイト自体、執筆者である高澤がWordPressでコーディングしました！

立ち上げた理由としては本当に色々あるのですが、シンプルに自分の知り得る知識やこれまでの経験を言語化して届けることで、何かの問題解決になれば嬉しいと思ったのがまず一つです。

また、これまでたくさん世の中の大変優秀な技術ブログにお世話になってきたので、自分も少しでもお力になれればと思ったというのもあります。

もしCMSでサイト構築のお仕事をされている方や初心者の方で今学ばれている方、またスマホアプリに関することを知りたい方がいらっしゃいましたら、当ブログをご活用いただけましたら嬉しいです！

↓こちらなのでよろしければチェックしてみてください！

https://ume-noki.com/

https://ume-noki.com/tech/

# 今後の方針について
今後はもし需要がありそうであれば、iOSアプリ開発のSwiftやサイト制作のWordPress、Shopifyなどの記事をQiitaへ投稿し、合わせて自分のブログサイトにもどんどん投稿して、情報発信を通じてエンジニアのみなさんの問題解決への材料を提供できればと考えております。

もちろん学習と開発も並行してどんどん進めていきたいと思っておりますので、みなさんと共に成長していければと思っております！

何卒よろしくお願いいたします！
DojangoでTemplateDoesNotExistと言われた

## 状況
Djangoチュートリアル中に`TemplateDoesNotExist`と言われた

## 原因
ファイル構成が間違っていた。本来は
```
samples
|--polls
     |--templates
          |--polls
              |--index.html
```
のところを
```
samples
|--polls
     |--templates
        　　|--index.html
```
にしていた

## 解決方法
ファイル構成を直した

## 参考元
- https://udomomo.hatenablog.com/entry/2018/08/14/234153
- https://docs.djangoproject.com/ja/5.0/intro/tutorial03/
deck.glを使ってGoogle Mapにベクトルタイルを描画してみる

## 概要説明
前回の記事では、Vector-Tiles-Google-Mapsを用いてGoogle Mapにベクトルタイルを描画させ問題点も紹介しましたが、今回はdeck.glを使ってみます。

https://qiita.com/yan_x/items/84a4e1358ec6393100e3

## deck.gl
deck.glについては、下記の記事を参考にしました。

https://qiita.com/4gou/items/da2546c2f2bd4f996bf0

## 前提条件
- Google Mapの表示にはGoogle Mapの APIキーが必要です
- ベクトルタイルについては、[TerraMap API](https://www.mapmarketing.co.jp/terramap-api?grid=qiita&utm_source=qiita.com&utm_medium=referral&utm_campaign=qiita_deckgl_vector_176ab38c8df090cbdfd0_01)の町丁目ポリゴンデータをベクトルタイル配信にしたものです。ベクトルタイルの表示には、deck.glのMVTLayerを用いています

## 描画してみる
```js
// 事前に npm install deck.gl でdeck.glをインストール
// deck.glのMVTLayerとGoogleMapsOverlayを使用する

import { MVTLayer } from '@deck.gl/geo-layers';
import { GoogleMapsOverlay } from "@deck.gl/google-maps";

(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
        ({key: "YOUR-GOOGLE-MAP-API-KEY",
            // Add other bootstrap parameters as needed, using camel case.
            // Use the 'v' parameter to indicate the version to load (alpha, beta, weekly, etc.)
        });

let map;

async function initMap() {
    const { Map } = await google.maps.importLibrary("maps");

    map = new Map(document.getElementById("map"), {
        center: { lat: 35.68437, lng: 139.75247 },
        zoom: 10,
    });  

    const layer = new MVTLayer({
      id: 'MVTLayer',
      data: [
        //pbfや任意のベクトルタイル配信サーバーのURLに変更可
        'http://localhost:3000/tiles/{z}/{x}/{y}'
      ],
      minZoom: 0,
      maxZoom: 12,
      getFillColor: [120, 150, 180, 220],
      getLineWidth: 10,
      getLineColor: [220, 220, 220],
      pointRadiusUnits: 'pixels',
    });
    

    const overlay = new GoogleMapsOverlay({
      layers: [layer],
    });
    overlay.setMap(map);
}

initMap();
        
```
実行結果
スムーズに描画されました。
![qiita6 (1) (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2916958/c95c468f-2965-0682-6551-f4723b4322dd.gif)

## まとめ
deck.glは信頼性があり、ベクトルタイルにおいてもMVTLayer、TileLayer、Tile3DLayerなど、さまざまな既製レイヤが用意されています。操作性もよいのでGoogle Mapにベクトルタイルを描画する有効な方法だと思います。
LLMでテーブルデータを読み取るアプローチ

ChatGPTやAzure OpenAI Service等のLLMのプロンプトで扱えるデータは一般的には文字列のみで、通常はExcelで作成したようなテーブルデータをそのまま読み取ることはできません。

しかし、テーブルデータをMarkdownやHTML形式の文字に変換することによりLLMが内容を理解することができます。

今回は、上記2つの形式を扱ったテーブルデータの読み取りについてご紹介します。

※本記事で利用したモデルはAzure OpenAI Serviceのgpt-35-turboとなります

## Markdown形式
まずは[Markdown](https://qiita.com/Qiita/items/c686397e4a0f4f11683d#%E6%89%8B%E5%8B%95%E3%81%A7%E5%85%A5%E5%8A%9B%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88)を活用したテーブルデータの読み取りについてご紹介します。

Markdownとは簡単な記号でリスト・リンク・テーブル等を表現する記法で、QiitaなどのブログやVisual Studio Codeなどのエディタ等でサポートされています。

Markdown形式の場合、後述のHTMLと比べて文字数が少ないためトークン数が節約できます。

試しに、以下のテーブルを読み取らせてみます。

### テーブルデータ
* テーブルイメージ

  ![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/b677aa77-4c39-3322-fffe-ac5833326aa0.png)


* Markdown
    ```
    | 部署 | 東京本社 | 横浜支社 | 大阪支社 |
    | - | - | - | - |
    | 開発部 | 50 | 20 | 25 |
    | 営業部 | 30 | 15 | 20 |
    | 経理部 | 20 | 10 | 15 |
    ```

### 回答結果
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/2941aad6-4764-ef28-cde9-e6df46f88c29.png)

どちらの質問も適切に回答できているため、Markdown形式で記述した文字をテーブルとして理解できていますね。

## HTML形式
次に、[HTMLのTableタグ](https://developer.mozilla.org/ja/docs/Web/HTML/Element/table)を活用したテーブルデータの読み取りについてご紹介します。

HTMLを読み取る強みとしては、複数セルを統合しているようなデータ（[`colspan`や`rowspan`](https://developer.mozilla.org/ja/docs/Web/HTML/Element/td#%E5%B1%9E%E6%80%A7)）に関しても読み取ることもできます。

試しに、以下のテーブルを読み取らせてみます。

### テーブルデータ
* テーブルイメージ

    ![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/341cc52c-9c23-d1a5-14bd-0d34d746f690.png)

* HTML
    ```html
    <table border="1">
    <tr>
        <th>部署</th>
        <th>性別</th>
        <th>東京本社</th>
        <th>横浜支社</th>
        <th>大阪支社</th>
    </tr>
    <tr>
        <td rowspan="2">開発部</td>
        <td>男性</td>
        <td>30</td>
        <td>10</td>
        <td>15</td>
    </tr>
    <tr>
        <td>女性</td>
        <td>20</td>
        <td>10</td>
        <td>10</td>
    </tr>
    <tr>
        <td rowspan="2">営業部</td>
        <td>男性</td>
        <td>20</td>
        <td>10</td>
        <td>15</td>
    </tr>
    <tr>
        <td>女性</td>
        <td>10</td>
        <td>5</td>
        <td>5</td>
    </tr>
    <tr>
        <td rowspan="2">経理部</td>
        <td>男性</td>
        <td>15</td>
        <td>5</td>
        <td>10</td>
    </tr>
    <tr>
        <td>女性</td>
        <td>5</td>
        <td>5</td>
        <td>5</td>
    </tr>
    </table>
    ```

### 回答結果
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/30958261-cc3a-621a-8951-2b2a70929412.png)

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/ae36d3ef-e697-7ea1-22ab-d704fb46feb9.png)

Markdown形式での結果同様に、HTMLで記載された文字をのテーブルとして理解できています。

さらに、複数セルを統合した情報（今回の例では部署情報）も読み取れています。

## まとめ
LLMでは、MarkdownやHTML形式に変換することでテーブルデータを読み取ることができます。

今回扱ったテーブルデータ程度であればどちらでも精度は大きく変わりませんが、トークン数を節約したいのであればMarkdown、複数セルを統合したテーブルを扱いたい場合はHTMLが適していると思います。
ASP.NETのセッション管理をRedis(Azure)で行う

必要に迫られてAPS.NETのセッション管理をRedis(Azure Cache for Redis)を使う手順を検証してみました。

## 環境

- VisualStudio Community 2022
- .NET Framework 4.7.2 

## WebForm1.aspx

プログラムとしては、サンプル的にFormにUserNameとEmailをSessionに入れるだけのものを作ってみました。確認のためページロード時にLabelへ代入して現在の値を表示しています。UI側は省略します。

```vb:WebForm1.aspx.vb
Imports System.Reflection.Emit
Public Class WebForm1
    Inherits Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        If Not Session("UserName") Is Nothing Then
            lbUserName.Text = Session("UserName")
        End If
        If Not Session("Email") Is Nothing Then
            lbPassword.Text = Session("Email")
        End If
    End Sub

    Protected Sub Button1_Click(sender As Object, e As EventArgs)
        Session("UserName") = tbUserName.Text
        Session("Email") = tbEmail.Text
        Response.Redirect("WebForm1.aspx")
    End Sub

    Protected Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Session.Abandon()
        Response.Redirect("WebForm1.aspx")
    End Sub
End Class
```

普通に動きます。特に何も設定していないのでSessionはInProc（ローカルに格納されている）状態になっているかと思います。これだと複数サーバーで動かした時にセッションが共有されないので、Redisを使えうように変更します。

## Azure側

-  [Azure Cache for Redis](https://azure.microsoft.com/ja-jp/products/cache)のインスタンスを作成
    - テスト目的なのでBasic C0で作成
        - 実際のところセッションキャッシュ利用でどのレベルが必要かは不明
        - 個人的にはキャッシュであればBASIC C1(1GB 1000接続)くらいで十分かと
    - 設定で特に迷いそうな部分無し
    - アクセス可能になるまで10-20分かかる（長い
    - 情報ソースは古いがそのまま動く

## プログラム側

- プロジェクトを右クリックして「NuGetパッケージの管理」から```Microsoft.Web.RedisSessionStateProvider```を追加

![スクリーンショット 2024-04-25 16.17.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14768/3b4caba8-4b4c-88f7-b580-7dc7aeaa0ce7.png)
**※ex版もあるが違いは不明**


- web.configに以下のsessionState情報を追加
    - hostは設定したもの、accessKeyは管理画面から取得できます

**※下記のテンプレートは自動追加されませんでした**
```xml
<configuration>
  <system.web>
    ....
    <sessionState mode="Custom" customProvider="MySessionStateStore">
        <providers>
            <add name="MySessionStateStore"
				type="Microsoft.Web.Redis.RedisSessionStateProvider"
				host="xxxxx.redis.cache.windows.net"
				port="6380"
				accessKey="xxxxxx"
				ssl="true" />
        </providers>
    </sessionState>
  </system.web>
...
<configuration>
```

これで同じように動作するのを確認します。普通に動けば設定はOKなはずです。動作していない場合や設定が誤っている場合は、タイムアウトしてエラーページが表示されます。

## ぶつぶつ

プログラム自体は変更することなく、Nugetでセッションプロバイダーの追加をして設定を書くだけで、Redis側に移りました。Redis側の設定も必要無く簡単でした。

気になる点
- Redisのコネクション数は同時？ Session管理だけの時は少なくて良い？
- 保存された値によるとTimeOutは20分(1200sec)
    - Redis固有でなく[HttpSessionState.Timeout](https://learn.microsoft.com/ja-jp/dotnet/api/system.web.sessionstate.httpsessionstate.timeout?view=netframework-4.8.1)の値の模様

## 参考

https://learn.microsoft.com/ja-jp/azure/azure-cache-for-redis/cache-aspnet-session-state-provider

https://learn.microsoft.com/ja-jp/training/modules/aspnet-session/

https://qiita.com/84zume/items/dfd9180a835b742eb4a3

https://kendik.hatenablog.com/entry/2014/09/29/022937

https://blog.shibayan.jp/entry/20220905/1662364264
【Atcoder】ABC 127 D問題


# コンテスト名

AtCoder Beginner Contest 127


https://atcoder.jp/contests/abc127

# 考えたこと
わからん。
とりあえず図を描いてみよう。
![IMG_0481.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775488/5afdc8b9-e84c-85b0-2303-439094d931df.jpeg)

最初に5,1,4のカードがあって、3のカード2枚、5のカード1枚と交換できるわけね。

カードの合計を最大にする訳だから、まずは1を捨てて3を仲間に入れよう。
![IMG_0482.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775488/fcae91b6-bad3-8725-d476-b62d796295f0.jpeg)
そうしたら3が1番小さいから5と交換して
![IMG_0483.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775488/d2966142-c7df-7012-03fa-9ce03b2fd411.jpeg)

...ん？

これって
[5,1,4,3,3,5]の配列から大きい順にN個抜き出せばいいだけじゃね？？？



```python
import heapq
n,m=map(int,input().split())
a=list(map(int,input().split()))
heapq.heapify(a)
l=[]

for i in range(m):
    b,c=map(int,input().split())
    l.append([b,c])
for i in range(m):
    a.extend(l[i][0]*[l[i][1]])
heapq.heapify(a)
for i in range(len(a)-n):
    heapq.heappop(a)
print(sum(a))
```
これで完璧！！
# ....と思ったが

![スクリーンショット 2024-04-25 11.13.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775488/9978c292-8535-7607-6b98-0ef21f1709af.png)

あれ。。。？
TLEだと。。。

# とりあえず調べる
``` python
for i in range(len(a)-n):
    heapq.heappop(a)
```
len(a)-n、すなわち追加要素の数を$B$とすると
for文で$O(B)$heappopで$O(log n)$(※1)なのでここだけで
計算量$O(Blog n)$かかる。

問題文を見ると
$1≤M≤10^5$
$1≤Bi≤N$
と書いてあるので$B$はとんでもなく大きい数字になる

# ランレングス圧縮
どうやら色々調べてみたところランレングレス圧縮(※2)とやらを使うらしい

例えば「AAAABBCC」という文字列があったとして
Aが4個、Bが2個,Cが2個なので「A4B2C2」と表す圧縮方法らしい

# これを応用しよう！
けんちょん様のブログを参考にさせていただきました。(※3)
先ほどは
```python
l.append([b,c])
```
としていましたが色々調べたところ、どうやらタプルを使って
```python
l.append((c,b))
```
とすることでcがb回連続していることを表現できるらしい。
# タプルって何？配列と何が違うの？
「同じ型のものを集める場合にはリスト、違う型のもの同士を集める場合にはタプル(※4)」
らしい。
なるほど、それならランレングス圧縮にぴったりな訳か。
```python
n,m=map(int,input().split())
a=list(map(int,input().split()))

l=[]
for i in range(n):
    l.append((a[i],1))
for i in range(m):
    b,c=map(int,input().split())
    l.append((c,b))
l.sort(reverse=True)

number_tmp=0
answer=0
for value,number in l:
    if number_tmp+number<=n:
        number_tmp+=number
        answer+=value*number
    else:
        answer+=value*(n-number_tmp)
        break
print(answer)
```       
        

    
    





# 参考
※1 https://qiita.com/uniTM/items/9630ed9d51f62b3e35ab
※2 https://qiita.com/wihan23/items/bbdb45e7e90ba27b9b02
※3 https://drken1215.hatenablog.com/entry/2019/06/15/021000
※4 https://qiita.com/keishi04hrikzira/items/167fbfdde101029c08da
パソコンの容量がいっぱいなので整理する（Windows）

# パソコンの容量がいっぱいになってしまった
## １．原因を調べる
どのファイルが大きいのか調べるため、下記のサイトのPower Shellのコマンドを参考にC\から大胆に実行する。（時間がかかります。）

https://orebibou.com/ja/home/201501/20150113_001/

```powershell
(Get-ChildItem "C\" -recurse -force |  Select-Object Fullname,Length  | Sort-Object Length -descending )[0..10]
```

## ２．実行結果からいらないファイルを削除する
### Outlookのメールを整理する
調べた結果、OutLookのメールが2番目に大きかったので古いメールの整理をした。いつか古いメールを調べるためにこの機能をオフにした気がする。


https://lanext.jp/one-man-it/outlook-archive/2337/
[Cloud Storage API(V2)] JavaでCloud Storageサービスアカウントのメールアドレスを取得する

Cloud Storage API V2(Java)を使用して、
Cloud Storageサービスアカウントのメールアドレスを取得する方法
についてご紹介します。

:::note info
APIを利用する環境の準備から始める場合や、コードを実行する際は、
[⧉[Cloud Storage API(V2)] JavaでCloud Storageを使う](https://qiita.com/doran/items/2a5f7ef8070fd5b8c94d)
を参照ください。
:::

|No|||目次|
|---|---|---|---|
|1|||[メールアドレスを取得](#1-メールアドレスを取得)|
||1||[スコープ](#11-スコープ)|
||2||[実行](#12-実行)|
||3||[レスポンスの内容](#13-レスポンスの内容)|

## 1. メールアドレスを取得
Cloud Storageサービスアカウントのメールアドレスを取得します。

### 1.1. スコープ
このAPIを実行するには、以下のいずれかのスコープを指定してください。
```
StorageScopes.CLOUD_PLATFORM
StorageScopes.CLOUD_PLATFORM_READ_ONLY
StorageScopes.DEVSTORAGE_FULL_CONTROL
StorageScopes.DEVSTORAGE_READ_ONLY
StorageScopes.DEVSTORAGE_READ_WRITE
```
[⧉[Cloud Storage API(V2)] JavaでCloud Storageを使う(2.2 Storageインスタンスを取得)](https://qiita.com/doran/items/2a5f7ef8070fd5b8c94d#22-storageインスタンスを取得)
でスコープを指定してください。

### 1.2. 実行

```java
public static void main(String[] args) throws Exception{
    Storage storage = getStorage();
    Storage.Projects projects = storage.projects();
    Storage.Projects.ServiceAccount account = projects.serviceAccount();
    Storage.Projects.ServiceAccount.Get get = account.get("プロジェクトID");
    
    ServiceAccount res = get.execute();
    System.out.println(res);
}
```
#### 1.2.1. HTTPリクエスト
GET:　https:\//storage.googleapis.com/storage/v1/projects/{プロジェクトID}/serviceAccount
が実行されます。

#### 1.2.2. クエリパラメータ
クエリパラメータはありません。

### 1.3. レスポンスの内容
#### ServiceAccount
|メソッド|戻り値|説明|
|---|---|---|
|getEmailAddress|String|Cloud Storageサービスアカウントのメールアドレス|
|getKind|String|リソースの種類<br>固定文字列："storage#serviceAccount"|

<br>
<br>
おしまい。。
Playwright - Javascript の Alert を検証する 

# 概要

デフォルトではPlaywrightは自動で Javascript の Alert を Accept するようだ

https://playwright.dev/docs/dialogs

# コード例

次のように書くとAlertのメッセージ内容を検証した上で、 Accept できる


```ts
  page.on('dialog', (dialog) => {
    expect(dialog.message()).toContain('アラートに表示されるメッセージ')
    dialog.accept()
  })

  await page.getByRole('button').click() // 押すとAlertが出るボタンを押す
```


# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
picoCTF 2021 writeup Transformation

## Transformation (Reverse Engineering)

I wonder what this really is... enc ''.join([chr((ord(flag[i]) << 8) + ord(flag[i + 1])) for i in range(0, len(flag), 2)])

添付ファイル
・enc

encをテキストエディタで開く。
```
灩捯䍔䙻ㄶ形楴獟楮獴㌴摟潦弸彥ㄴㅡて㝽
```
問題文のプログラムは、flagを2文字ずつとって、bitで結合させているらしい。テキストエディタで出てきた文字列は結合した16bitsを文字変換したものと思われる。
とりあえず、文字列を数字に直すと、
```
7069 636f 4354 467b 3136 5f62 6974 735f 696e 7374 3334 645f 6f66 5f38 5f65 3134 3161 3066 377d
```
これを2進数になおして、
```
0111000001101001 0110001101101111 0100001101010100 0100011001111011 0011000100110110
0101111101100010 0110100101110100 0111001101011111 0110100101101110 0111001101110100
0011001100110100 0110010001011111 0110111101100110 0101111100111000 0101111101100101
0011000100110100 0011000101100001 0011000001100110 0011011101111101
```
これを上下8bitsずつに分け、文字に直すと、

`picoCTF{16_bits_inst34d_of_8_e141a0f7}`
空のディレクトリをGitHubへcommitする方法

<h2>本日のお悩み</h2>

gitへpushし動作確認をした際、ディレクトリがないよ〜と言われてしまったのでその時の対処方法を記載します。

作成したwebアプリをデプロイする際にプロジェクトをgitへpushするかと思います。
その際、特定のディレクトリを参照する機能があれば、もちろん、そこを探しに行くかと思いますが、今回そのディレクトリにはデータが入っていない空の状態でした。

どうやらGitではディレクトリは作成してあっても**中身が空**だと無かったことにされてしまい、commitから除外されてしまうみたいです。

では、どうすれば空のディレクトリを認識しcommitしてくれるのか。すごく簡単です下記に記載しました。

<h2>本日の処方箋</h2>

じゃあどうすればいいかって、commitされなかった空のディレクトリに

<h3>.gitkeep</h3>

ただこれを作成するだけ！
そうすると、.gitignoreのファイルと同じようなファイルができるのでそれで初めてcommit対象になります。超簡単。

下記例

```shell_session
$ cd 空の対象ディレクトリ
$ touch .gitkeep
```

最後に作成できたらGitへの更新もお忘れなく！👋

```shell_session
$ git add .
$ git commit -m "secondcommit"
$ git push -u origin main
```
AWS 認定

# 筆者のスペック
- 文系大学を中退
- 25歳で営業職からネットワークエンジニアに転職 2022年
- ネットワークエンジニアを1年経験し、クラウドに惹かれクラウドを専門にするため現自社開発企業に入社 2023年

## 保有資格
|AWS Certified Cloud Practitioner|AWS Certified Solutions Architect - Associate|AWS Certified Solutions Architect - Professional|AWS Certified Developer - Associate|
|:---|:---|:---|:---|
|Active Date 2022-12|Active Date 2022-12|Active Date 2024-02|Active Date 2024-03|

## AWS認定を取得しようと思ったキッカケ
最初はAmazonが提供するサービスということくらいしかしりませんでした。

私が寒いサーバールームで汗をかきそうになりながらサーバーラックにネジを巻いている話をした時に、友人にAWSの便利さを教えてもらいました。

その時に是非仕事にしたい。と思いましたね。

ネットワークエンジニア時代の職場でもAWSの案件はたまにある。
と聞いたので、その時にアサインされるようにまずは基礎の資格CLF (AWS Certified Cloud Practitioner)を取りましたね。

この資格勉強がおもしろく、続けてSAA(AWS Certified Solutions Architect - Associate)の資格を取得しました。
[公式](https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/)の受験対象に全て当てはまっているわけではありませんが、
実際に受けてみて`興味とクラウド分野の勉強が楽しい。と思う気持ちさえあれば受験対象に当てはまる`と思いました

私が在職していた期間には残念ながら、AWSの案件はなく転職しました。

そして現職に転職したわけですが、個人的には技術レベルが非常に高く、とても楽しい職場です。
経験の浅い私にもアーキテクトを考える機会を与えてくれる素晴らしい会社ですね。

浅いきっかけですが
実際にアーキテクチャを考えるとこれがこれまた楽しく、上位資格のSAP(AWS Certified Solutions Architect - Professional)もとろう！と思いましたね。

#### AWS認定を取得してよかったこと/取得を勧めたい理由
ネットワークエンジニアの頃にSAAを取得していたことで、現職場でアーキテクチャを考える機会を頂けたと思っています。

ただおもしろいから取る。でも充分ではあると思いますが、
私のような経験の浅い方には非常におすすめです。

経験から得る知識が1番ですが、**経験を得るための知識**も重要だな―と思いました。
やりたいです！だけで任せてもらえる環境が1番なのですが、自分が`クラウド技術が好きです。`というアピールが簡単にできますからね。

今ではSAPの受験費用は44000円です。
なかなか受けようと思う値段ではありませんよね。
私の現職場では合格したら資格代は帰ってくる制度でしたので、本気でうけました！1度落ちてさらに本気になりました！ｗ

好きと言っている、自主的に資格取ってる、というだけで一緒にやってみる？というチャンスが舞い降りてくる確率は高くなると私は思っています。

なのでこれからクラウド技術をもっと経験したい。という方には**AWS認定**はぴったりなので是非とってください！！

### 資格に意味はない？
資格は意味がない。という意見はよく聞きますが、確かに資格を取っただけでは突然業務ができるわけではないのである意味その通りです。
ただ業務をこなしていくうえで、理解するときの助けにはなります。
知識はいくらあっても無駄ではありませんですからね。


#### 最後に
今回この記事を書こうと思ったのは**資格とるのもありだよー！**というよりマークダウンの練習のためでした。

私は1番大切なのは**経験**だと思います。
資格は経験を得るための大きな1歩だと思うので、とりあえずAWS認定資格は全部取ろうと思ってます。
こんな意見もある。くらいの参考になれば嬉しいです。


追記
マークダウン、terraformよりよくわからない。
Nuke が CocoaPods のサポートを終了した話

内容としては2年ほど前だけど、Qiitaにそれ関連の記事がなかったので投稿します。

# CocoaPods のサポートを終了
Nuke は、v10.7.1 を最後に CocoaPods による配布サポートを終了しました。
> CocoaPoads support is no longer available. Carthage should still work, but is not supported.

https://github.com/kean/Nuke/issues/581#issuecomment-1202375861

現在、CocoaPods から Nuke を入れようとすると、[v10.7.1](https://github.com/CocoaPods/Specs/blob/efb8d93eca5826423320598b1bf6dd770986d93e/Specs/3/d/e/Nuke/10.7.1/Nuke.podspec.json#L3) までのバージョンしか利用することができません。

https://cocoapods.org/pods/Nuke

最新の Nuke を使用する場合は、[Swift Package Manager(SPM)](https://swiftpackageindex.com/kean/Nuke) or [Carthage](https://github.com/Carthage/Carthage) を使用する必要があります。


# 開発終了の背景
Nuke のメンテナーの kean 氏曰く、SPM や Carthage では問題なく動作する一方、CocoaPods特有のバグへの対応に多くの時間を割くことになっており、これが開発終了に繋がったと報告されています。

https://github.com/kean/Nuke/issues/581#issuecomment-1202428255