OmniAICreator/Qiita-1.07M huggingface dataset:
C#のDateTime型のAddMonthsで月末の日に1ヵ月を加算する場合の月末に丸められる動作

# まとめ(実測結果)
* C#のDateTime型のAddMonthsで月末の日にNヵ月を加算すると、
  * 加算後の日付が月内を超える場合は月末の日に丸められ(1/31(月末)→2/28(丸め)等)、
  * 月内を超えない場合は加算前と同日を維持する(2/28(月末)→3/28(同日、非月末)等)

# テストコード
```C#:Program 2301-2 AddMonthsの月末丸め動作テスト.cs
using System;

namespace ConsoleApp1 {
    class Program {
        static void Main(string[] args) {
            /* 結果: 
DateTimeのAddMonthsで月末の日にNヵ月を加算すると、
加算後の日付が月内を超える場合は月末の日に丸められ(1/31(月末)→2/28(丸め)等)、
月内を超えない場合は加算前と同日を維持する(2/28(月末)→3/28(同日、非月末)等)

2023/02/28 0:00:00
2023/03/31 0:00:00
2023/03/28 0:00:00
2023/05/30 0:00:00
2023/05/31 0:00:00
            */

            Func<string, DateTime> DT = DateTime.Parse;
            Console.WriteLine($"{DT("2023/01/31").AddMonths(1)}");
            Console.WriteLine($"{DT("2023/01/31").AddMonths(2)}");
            Console.WriteLine($"{DT("2023/02/28").AddMonths(1)}");
            Console.WriteLine($"{DT("2023/04/30").AddMonths(1)}");
            Console.WriteLine($"{DT("2023/01/31").AddMonths(4)}");
        }
    }
}
```

# 環境
Microsoft Visual Studio Community 2019 Version 16.11.22
VisualStudio.16.Release
Microsoft .NET Framework
Version 4.8.04084
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/a80b2b96-ebec-1c6b-a5a2-700f34441206.png)
Rのサフィックス（接尾辞）”L”についてのメモ

# はじめに
少し調べたことをメモしただけなので、間違いがあればご指摘貰えると助かります。
パッと調べたところ日本語では纏まっていないようでした。

# 本編
Rで関数の使用例を見ていると、たまに数字の後ろに”L”がついていることがある。

> **Usage**
> lag(x, n = 1L, default = NA, order_by = NULL, ...)
> lead(x, n = 1L, default = NA, order_by = NULL, ...)
> <cite>https://dplyr.tidyverse.org/reference/lead-lag.html</cite>より

このLがよく分からず、n=1とかでも問題なく動くので放置していたが気になったので調べてみた。

調べてみると、https://cran.r-project.org/doc/manuals/r-release/R-lang.pdf に答えがあった。
該当箇所（14ページ目）をDeepLに突っ込んだものを引用する。

> L'サフィックスは、任意の数値を明示的に整数にする目的で使用することができます。つまり、'0x10L' は16進数表現から整数値16を生成します。定数 1e3L は 1000 を数値ではなく整数として与え、1000L と等価です。(もし、1e-3Lのような整数値でない値をLで修飾すると、警告が表示され、数値が生成されます。また、1.Lのように小数点が不要な場合も警告が発生します。Lを複素数で使用するとシンタックスエラーが発生します。定数はかなり退屈なもので、もっといろいろなことをするにはシンボルが必要です。

実際に動かしてみると、以下のような形になる。サフィックスLをつけるとintegerとして扱われるようだ。
小数であればWarningが出て、複素数であればerrorになる。
```R
> a <- 2023
> a
[1] 2023
> b <- 2023L
> b
[1] 2023
> typeof(a)
[1] "double"
> typeof(b)
[1] "integer"
> hoge <- 0x10L
> hoge
[1] 16
> typeof(hoge)
[1] "integer"
> fuga <- 1e3L
> typeof(fuga)
[1] "integer"
> piyo <-1e-3L
Warning message:
non-integer value 1e-3L qualified with L; using numeric value 
> typeof(piyo)
[1] "double"
> dame <- 12iL
Error: unexpected symbol in "dame <- 12iL"
```

# 結論
Lは整数であることを示すためのサフィックスである。
関数のHELPででてくるのは、その引数が整数であることを明記したいからなのだろう。
【TypeScript】ブラウザの標準機能だけでPKCE認証のためのコード生成は実装できる

## PKCE対応のためのcode_challenge生成をフロントエンドで行いたい

- PKCE対応のためのコード生成をフロントエンド側で行う必要が出てきた
- sha256のハッシュ化やランダムな文字列生成のためのライブラリは世にたくさんあるが、外部ライブラリを利用しなくとも実装は可能であると分かった
    - 具体的にはweb crypto APIというブラウザ内の機能を使う。その説明はこの記事が詳しい
        - [Web Crypto APIについて](https://zenn.dev/riku/articles/7e7151b22e0da9)
- 紛らわしいのは、`node.js`にも`cyrpto`というAPIが存在すること
    - ここに私はひっかかった。`node.js`の`crypto`のAPIで実装を進めてしまっていた。`node.js`なのでブラウザでは直接実行できない。実行するためには`Browserify`、`streamify`というツールのインストールが必要になってしまう
    - `Buffer`を使ったエンコード例の記事がweb上には多い。しかし、`Buffer`はブラウザでは標準非対応である。ブラウザで動かしたいなら利用を避けたほうがいい
    - 下記の記事が、両ライブラリの違いについて詳しい
    - [[Node.js][JavaScript]CryptoAPIの違いでハマったのでまとめ](https://qiita.com/qrusadorz/items/3521f90a727fef61ae63)
- ブラウザでも標準ライブラリだけで、PKCE認証のためのコード生成は行えることを、主要なメッセージとして本記事を投稿する
        
## 具体的なコード例

- ここで私の実装例を書きたいところだが、実際は[WouterSpaak](https://github.com/WouterSpaak)氏のコード例をほとんど参考にした。WouterSpaakさん、本当にありがとうございます
    - WoutrerSpaak氏の[Stackblitz上で彼のコードが確認できる](https://stackblitz.com/edit/typescript-sha256-base64-encoded?file=index.ts)
- コード例は大体下記のような感じである
```typescript:challenge-code-service.ts
const private _code_verifier;

get code_verifier {
  return _code_verifier;
} 

//このメソッドをcode_challengeが欲しいときに呼び出す
createCodeChallengeAsync() {
  _code_verifier =  randomStr(43); //PKCEでのcode_verifierは43文字以上128文字以下である必要がある
  const shaBuffer = await sha256(code_verifier);
  const encoded = bufferToBase64UrlEncoded(shaBuffer);
  document.getElementById('encoded').textContent = encoded;
}

randomStr(len: number) {
  const arr = new Uint8Array(len);
  window.crypto.getRandomValues(arr);
  return String.fromCharCode(...toCharCodes(arr));
}

toCharCodes(arr: Uint8Array) {
  const validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_~.';
  return arr.map(x => validChars.charCodeAt(x % validChars.length));
}

sha256 (message: string) {
  const encoder = new TextEncoder();
  const data = encoder.encode(message);
  return window.crypto.subtle.digest('SHA-256', data);
}

bufferToBase64UrlEncoded(input: ArrayBuffer) {
  const bytes = new Uint8Array(input);
  return urlEncodeBase64(window.btoa(String.fromCharCode(...bytes)));
}

urlEncodeBase64(input: string) {
  return input.replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/\=/g, '');
}
```

- 工夫が必要なのは以下の機能である。すべてブラウザ搭載である`window`および`window.crypto`に紐づく関数を利用して実装できていることが確認できる
    - ランダムな文字列の生成
    - BASE64のエンコード
    - sha256によるハッシュ化
- トークン取得のPOST時に、暗号化するまえの元の文字列である`code_verifier`が必要になる
    -  こちらはメモリ上に持つ形として、ブラウザ内のストレージには保管しないこととした。破棄のタイミングの検討が必要になるため。加えて、この機能を開発したアプリケーション上、保持したcode_verifierを破棄するようなユーザー操作は想定されない。すぐにPOSTする操作が予想されるため、メモリ上に保持しておくことでも大きな問題は無いと判断している
-  BASE64でエンコードするために、作った`string` を一度`ArrayBuffer`に戻す必要がある
SourcetreeでClone時に有効なパス/URLではありませんと表示された時の対応方法

いくつか方法を試したのですが、この方法で解決したため、備忘録として残す。

## 発生した事象
Macでsourcetreeを使用し、認証方法としてrepoとuser権限のpersonal_access_tokenを発行し、
下記のパスワードにトークンを入力する方法でアカウントを登録

![スクリーンショット 2023-01-28 11.46.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297660/662deb0d-1e2c-02d3-8540-5a7d4c83de03.png)

remoteには各種ブランチが表示されるが、自身のブランチ含めて全て「有効なパス/URLではありません」と表示されクローンができない。ターミナルからgit cloneのコマンド等を使用すれば、clone自体はできる。

## 解決方法
設定->Git内のGitのバージョンからシステムのGitを使用するで、そのままGitを指定して開くを設定すると直りました、

![スクリーンショット 2023-01-28 11.53.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297660/6e6558b6-f395-3bbd-1933-f6a341dfbfff.png)


## 試したけど直らなかった方法

設定の言語設定をEnglishに変更後に再起動し、再度設定を開き、Advancedの設定で過去の設定を削除する
->Advancedの設定は言語設定日本語では開けないため英語で開く必要があります。Advanced内に、過去の設定は残っていなかったため、直らず。

Source Treeの初期化(~/Library/Application\ Support/SourceTree/のファイル削除)
->削除して、再度アカウント設定を実施しても直らず。
neosでlogix その101

# 概要
neosでlogixやってみる。
logixを、俺コードで表現してみる。
練習問題やってみた。

# 練習問題
FM変調を表示せよ。

# 俺コード

```
Int.s = T
	|> Sin
	|> X 6
Int.h = T
	|> X 12
DisplayGraph = + Int.s Int.h
	|> Sin

```

# 写真

![2023-01-28 02.37.28.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/8990e14c-7b91-245d-fc3d-3ac61c43d116.jpeg)


以上。
neosでlogix その100

# 概要
neosでlogixやってみる。
logixを、俺コードで表現してみる。
練習問題やってみた。

# 練習問題
AM変調を表示せよ。

# 俺コード

```
Int.s = T
	|> Sin
	|> X 6
	|> + 1
Int.h = T
	|> X 12
	|> Sin
DisplayGraph = X Int.s Int.h

```

# 写真

![2023-01-28 02.14.40.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/ebdcbc82-cc0a-0696-9b04-900ef3a8b7f9.jpeg)


以上。
Slack::Notifierをrspecでmockする

SlackNotifierのmock方法の記事が見つからなったため書いてみました。

```rb
let(:slack_mock) { double('slack') }

subject { #処理を定義 }

# pingレシーバーを定義
before(:each) do
  allow(Slack::Notifier).to receive(:new).and_return(slack_mock)
  allow(slack_mock).to receive(:ping)
end

it 'Slack通知が実行されない' do
  expect(slack_mock).to receive(:ping).never
  subject
end

it 'Slack通知が１回呼び出される' do
  expect(slack_mock).to receive(:ping).once
  subject
end

# メッセージまで検証したい場合は下記の通り

it 'Slack通知に想定メッセージが渡され実行される' do
    expect_message = "test"
    expect(slack_mock).to receive(:ping).with(expect_message).once
  subject
end

```

参考記事

https://qiita.com/ham0215/items/f0564c9933e573834eae
【UiPath】設定ファイルの書き方（番外編）

# はじめに
この投稿は、RPAツール「UiPath」での 設定ファイルの書き方 について「個人的に」まとめたものです。

以前に似たような記事を書きましたが、そこには書いてない「番外編」として紹介します。

https://qiita.com/miya_gis/items/35a4ee5d1806299e33e2

https://qiita.com/miya_gis/items/c63fd526b58f0db655bb

# 値をエクセル上で計算

UiPathでは設定ファイルは「Config.xlsx」としてエクセルで作成し「範囲を読み込み」アクティビティで表形式の設定を読み取ることが主流です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/26f470f4-bd0c-1562-56ea-db2ddf3250fc.png)
この「範囲を読み込み」アクティビティは、セル内に数式が設定されていると、数式の結果を読み取ります。例えば以下のように
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/876f2ebc-4f26-a6f9-8448-95b9af7b6f81.png)
と書くと、読み込んだタイミングでの現在日付がセットされます。

もう少し頑張ると、以下のように設定ファイル上で「日付を簡単に変える」事も出来て、
テストする際や、リカバリ実行時に使えます。

↓ 加算日数を変更すれば、ファイル名が変えられる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/efbc97f9-6d09-2779-57b4-a04e4f0acffc.png)


もちろん「Xaml上で計算するようにしても良い」のですが、Excel上で計算させると「Xamlを直さなく良い」ので「楽」です。

# 気をつけること

Excel上で計算できるからといって、あまり複雑にすると計算がおかしくなる可能性があります。

例）「年またぎ」をする際に、計算式の考慮が出来ていなくて年月がおかしくなってしまう

⇒ 月移動なら「EDATE」関数を使うと簡単に計算できます。

https://support.microsoft.com/ja-jp/office/edate-%E9%96%A2%E6%95%B0-3c920eb2-6e66-44e7-a1f5-753ae47ee4f5

⇒ 加算減算の値を変化させて（日付をズラして）「年をまたいでも計算が正しいことを確認する」等をすれば、見えないバグを発見できます。

あんまり「Excel上で頑張らない」ようにする、頑張るならしっかりつくる、そして「検証をしっかりする」必要があります。


# 終わりに
以上、設定ファイルの書き方のTipsでした。
この記事が参考になったら、 LGTMをお願いします。閲覧ありがとうございました。
SourceTreeからBitBucketにパスフレーズの入力なしで接続する

# 環境情報
||バージョン|
|---|---|
|OS|macOS12.1|
|SourceTree|4.2.1|

## 前提条件
SSH公開鍵・秘密鍵を作成済であること

||ファイル|
|---|---|
|秘密鍵|~/.ssh/id_rsa_bitbucket.pub|
|公開鍵|~/.ssh/id_rsa_bitbucket|


## BitBucketでの設定
### 1. Personal setting -> セキュリティ -> SSH鍵を開く

![スクリーンショット 2023-01-28 10.29.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639200/8bf43c56-0fbf-a7fe-f109-b8c8e9ca1647.png)

### 2. SSH公開鍵を登録する
![スクリーンショット 2023-01-28 10.29.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639200/9f86ee73-fc76-bdae-e128-16d411e38f63.png)


## Macでの設定
### 1. キーチェーンに秘密鍵を登録する
```
ssh-add ~/.ssh/id_rsa_bitbucket
```
### 2. configファイルを生成し、パーミッションを設定する
```
$ touch ~/.ssh/config
$ sudo chmod 700 ~/.ssh
$ sudo chmod -R 600 ~/.ssh/*
```

### 3. configファイルに以下を保存する
```
# BitBucket
Host bitbucket.org
  User git
  HostName bitbucket.org
  IdentityFile ~/.ssh/id_rsa_bitbucket
  IdentitiesOnly yes
```

### 4. BitBucketにパスフレーズの入力なしで接続できることを確認する
```
$ ssh -T git@bitbucket.org
authenticated via ssh key.

You can use git to connect to Bitbucket. Shell access is disabled
```

### 5. SourceTreeでパスフレーズの入力なしでcloneできることを確認する
![スクリーンショット 2023-01-28 11.24.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639200/7222321e-d9ce-3682-41ea-1e2837e562ba.png)
Hexabase Flutter SDKで全文検索を行う

[Hexabase（ヘキサベース）](https://www.hexabase.com/)は企業向けのBaaS（Backend as a Service）を提供しています。認証やデータストア、ファイルストレージ、リアルタイム通知などの機能があり、企業向けのシステム開発に必要な機能をまるっと提供しています。

APIはREST APIとGraphQLを提供しています。そのAPIをラップしたSDKも開発を進めています。SDKは現在、TypeScriptとDart向けに開発しています。Dart向けのSDKは、もちろんFlutterでも利用できます。

この記事ではDart/Flutter SDKについて、全文検索に対応したので使い方を解説します。

## Hexabaseの全文検索について

Hexabaseで提供する全文検索は全部で3パターンあります。

- アイテム
- 添付ファイル
- 履歴コメント

全文検索を行う際には、いずれかを指定する必要があります。

## 全文検索の対象にする

フィールドの設定にて、 `全文検索の対象にする` をチェックしてください。これがないと、全文検索対象外になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/0ff919f5-f7de-8c02-d64a-213e79ecca39.png)


## パッケージとソースコード

パッケージはpub.devで公開しています。

[hexabase | Flutter Package](https://pub.dev/packages/hexabase)

ソースコードはGitHubにて公開しています。ライセンスはMIT Licenseです。

[hexabase/hexabase-dart: Hexabase SDK for Dart & Flutter](https://github.com/hexabase/hexabase-dart)

## インストール

インストールは `dart pub get` で行えます。

```
dart pub get hexabase
```

## インポート

SDKをインポートします。

```dart
import 'package:hexabase/hexabase.dart';
```

## 初期化

まずインスタンスを作成します。

```dart
var client = Hexabase();
```

別なファイルで再度インポートした場合には、以下でインスタンスを取得してください。

```dart
var client = Hexabase.instance;
```

## 全検索を行う

全文検索は、まず対象となるデータベースを指定します。

```dart
var project = client.project(id: "PROJECT_ID");
var datastore = project.datastore(id: "DATASTORE_ID");
```

そして検索を実行します。 `HBSearchType` には `history` の他、 `item` と `file` が用意されています。それぞれ対象を指定するのに利用します。

```dart
var response = await datastore.search(HBSearchType.history, "comment");
print(response.count); // Items count
print(response.items); // Items
```

## まとめ

Hexabaseでは全文検索にelasticを利用し、強力な全文検索を実現しています。データベースや全文検索を独自に構築するのは面倒ですが、Hexabaseを使えば瞬時に利用開始できます。

ぜひBaaSを使ってアプリ開発にチャレンジしてください。

[株式会社 Hexabaseー法人向けクラウドシステム『Hexabase』ー](https://www.hexabase.com/)
ALU Controlを実装していてパタヘネRISC-V版の誤りに気づいた件

パタヘネRISC-V版(David A. Patterson and John L. Hennessy: Computer Organization and Design: The Hardware/Software Interface: RISC-V Edition, Morgan Kaufmann Publishers, 2017)にしたがって，ALU ControlをSystemVerilogで実装したのですが，書籍の誤りに気づきました．

出来上がったコード

```SystemVerilog:alu_control_rv32i.sv
module alu_control_rv32i(
    input logic [31:0] inst,
    input logic valid_inst,
    input logic [1:0] alu_op,
    output logic [3:0] alu_ctl
);

    logic [3:0] f;

    always_comb begin
        f = {inst[30], inst[14:12]};

        case (valid_inst)
            1: begin
                begin
                    alu_ctl[0] = (alu_op[1] & (f[1] & ~f[0]));
                    alu_ctl[1] = (~(alu_op[1]) | ~(f[2]));
                    alu_ctl[2] = (alu_op[0] | (alu_op[1] & f[3]));
                    alu_ctl[3] = 0;
                end
            end

            0: begin
                alu_ctl = 0;
            end
        endcase
    end

endmodule
```

# 書籍の誤りについて

Appendix C: Mapping Control to Hardware の C.2 Implementing Combinational Control Units の記載のFigure C.2.3が誤っていました．どのように訂正するかについては，前述のSystemVerilogのソースコードから察してください．
windowsのGithubDesktopでsjisを表示させようとしてハマったので紹介します

# はじめに
VBAのマクロをGithubで管理しようとGitHub Desktopをインストールしたのですが、WEB上では正しく表示されるのですが、GitHub Desktopでは全角文字が文字化けしてしまいました。
それを解消する方法です。

手順２まではググれば出てきたのですが、そこまでの設定だと右側の差分を表示するエリアがクルクル回るカーソルが出たままで、ソースが表示されないという状態になりました。

いろいろ調べてPathが原因だと判明したので、同じように悩んでいる方のために情報を共有します。



# 手順

## 1 .gitattributes ファイルを作成
リポジトリのフォルダに 
.gitattributes ファイルを作成し、以下の内容を記載する

```.gitattributes
*.bas diff=sjis
*.cls diff=sjis
*.frm diff=sjis
```
左側は変換対象とするファイル名（ワイルドカード可）
sjisの部分は任意の名称

## 2 .gitconfig ファイルに記載を追加
C:\Users\\{ユーザ名}\\.gitconfig
ファイルに、以下のエントリを追加する
特定のリポジトリのみ適用する場合は
レポジトリ配下の**非表示**フォルダの.gitフォルダにあるconfigファイルを対象にする

```.gitconfig
[diff "sjis"]
	textconv = iconv -f sjis -t utf-8
```
"sjis"の部分は.gitattributes ファイルで決めた名称
意味：差分を表示するときに、「iconv -f sjis -t utf-8」のコマンドを実行しなさいということ
iconv はLINUXの文字コード変換コマンド

## 3 iconvコマンドにPathを通す
手順2まででうまくいかない場合
コマンドプロンプトを立ち上げて以下のコマンドを実行
```
ls | iconv -f sjis -t utf-8
```
※ ls は windowsの dir に相当するコマンド
エラーになった場合、iconv.exeのあるフォルダにPathを通す
私の環境では以下の通り
``` C:\Program Files\Git\usr\bin\iconv.exe ```

### Pathを通す方法
コントロールパネルを立ち上げる
右上の検索窓に「環境変数」と打ち込む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/bfbd089b-0c4f-a86d-7979-aaf651f8c43b.png)
「環境変数を編集」をクリックし表示された画面に従いPathを追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/d923df91-f240-ba95-8226-e3be819736f1.png)

再度コマンドプロンプトで以下のコマンドを実行し、iconvコマンドが有効になったことを確認
```
ls | iconv -f sjis -t utf-8
```
C#でWindowsForm上のコントロールをすべて取得する


# 1. はじめに
* WindowsForm上の全コントロールを取得して処理を共通化したい

# 2. クラスの作成
* WindwosフォームからControlを受け取ることで、フォーム上にあるコントロールを取得する

```csharp:
public static class GetControls
{
    public static void  Init<T>(this T control) where T : System.Windows.Forms.Control
    { 
        // コントロール全てを列挙
        List<Control> controls = GetAllControls<Control>(control);
        Debug.Print("controls : " + controls.Count().ToString());

        foreach (var ctl in controls)
        {
            // TODO: 仮実装
            Debug.Print("**********control : " + ctl.Name);
        }

        // ボタン、またはボタンを継承したコントロールを列挙
        List<Button> buttons = GetAllControls<Button>(control);
        System.Console.WriteLine("buttons : " + buttons.Count().ToString());

        foreach (var ctl in buttons)
        {
            // TODO: 仮実装
            Debug.Print("**********button : " + ctl.Name);
        }
    }

    /// <summary>
    /// 指定のコントロール上の全てのジェネリック型 Tコントロールを取得する。
    /// </summary>
    /// <typeparam name="T">対象となる型</typeparam>
    /// <param name="top">指定のコントロール</param>
    /// <returns>指定のコントロール上の全てのジェネリック型 Tコントロールのインスタンス</returns>
    private static List<T> GetAllControls<T>(Control top) where T : System.Windows.Forms.Control
    {
        List<T> buf = new List<T>();
        foreach (Control ctrl in top.Controls)
        {
            if (ctrl is T) buf.Add((T)ctrl);
            buf.AddRange(GetAllControls<T>(ctrl));
        }

        return buf;
    }
}
```

# 3. Windowsフォームを作成
* Form_Load時に作成したクラスのInitメソッドを呼びだすようにする

```csharp:
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 作成したクラスのメソッドを呼び出す
            this.Init();
        }
    }
```

# 4. 動作確認
* Windowsフォームに配置したコントロールが取得できることを確認

# 5. 参考文献
https://qiita.com/oo2kazuma/items/881a378146eed14e8ace
C#でカスタムコントロールに独自のプロパティを追加する

1. はじめに

* C#のカスタムコントロールに対して独自のプロパティを保持できるようにしたい
* 独自のプロパティはプロパティが展開するような作りにしたい
  * (例) サイズ内に幅、高さのプロパティを保持する

# 2. 動作イメージ
* デザインのプロパティウィンドウでカスタムコントロール毎にプロパティを保持できるにする

# 3. 独自プロパティ用クラスの作成
* クラスの先頭に ```  [TypeConverter(typeof(ExpandableObjectConverter))] ```  の属性を追加する
* プライベートな独自プロパティに対してパブリックなGet,Setメソッドを準備する
* ToStringメソッドの結果がプロパティ全体の表示に使用される


```csharp:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class MyProperty
{
    private int left;		// 独自プロパティ1
    private int top;		// 独自プロパティ2
    private int right;	// 独自プロパティ3
    private int bottom;	// 独自プロパティ4

    public MyProperty(int left, int top, int right, int bottom)
    {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }

    public int Left
    {
        get
        {
            return left;
        }
        set
        {
            left = value;
        }
    }

    public int Top
    {
        get
        {
            return top;
        }
        set
        {
            top = value;
        }
    }

    public int Right
    {
        get
        {
            return right;
        }
        set
        {
            right = value;
        }
    }

    public int Bottom
    {
        get
        {
            return bottom;
        }
        set
        {
            bottom = value;
        }
    }

    public override string ToString()
    {
        return String.Format("{0}, {1}, {2}, {3}",
           this.Left, this.Top, this.Right, this.Bottom);
    }
}

```

# 4. 独自プロパティを持つ カスタムコントロールの作成
* Labelクラスを継承してカスタムコントロールを作成する
* 独自プロパティ用クラスをプロパティに追加する
* 3行目に初期値を設定しているが、この行がないとGUIでプロパティが表示されないため注意する

```csharp:
public partial class LabelEx : System.Windows.Forms.Label
{
    private MyProperty _myProperty = new MyProperty(0, 0, 0, 0);

    public LabelEx()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe
    }

    public MyProperty MyProperty
    {
        get
        {
            return _myProperty;
        }
        set
        {
            _myProperty = value;
        }
    }
}
```

# 5. 動作確認
* カスタムコントロールに対してプロパティがセットできることを確認

# 6. 参考文献
http://acha-ya.cocolog-nifty.com/blog/2010/10/size-1-d2ef.html
【合格体験記】新卒がHTML5プロフェッショナル認定資格Level2 バージョン2.5に合格した

# はじめに
業務経験少しの新卒が該当試験に合格したので、
その際の勉強法等を書きます（一部レベル1体験記と記述重複する箇所があります）

https://qiita.com/tkdayo/items/7d5d63e9fc158ac9c88f

# 概要
公式ページ参照

https://html5exam.jp/outline/lv2.html

# 本人スペック
<ul>
<li>会社員歴半年弱の新卒SEです</li>
<li>就業先開発施策内でSpring BootとJS（ES6もそこそこに書ける）、各種マークアップを書いています。基本調べながらちょっと書けるくらい</li>
<li>業務外では「Astro」や「React,Next」使ったサーバレスの個人開発に関することの勉強をしています</li>
<li>基本情報取得済み、応用情報不合格、Java Silver、Webデザイナー検定エキスパート取得済み、該当試験レベル1取得済み</li>
</ul>

# 勉強期間
1ヶ月と1週間くらい、毎日2〜3時間

勉強期間はサイトによってまちまちですが、1ヶ月〜2ヶ月あればなんとかなると思います。
JSのAPI等に詳しければ、もっと短縮出来るかもしれないですね


# 取得点数
<b>75/100</b>

合格に必要な点数は7割と公式に記載あります、かなりギリギリでした。

# 受験した理由
<ul>
<li>就業先の受験料補填対象の資格だった</li>
<li>フロントエンドが好きでバックエンドより得意に書けるが、根拠となる実績がないので、その根拠の一端を担ってくれたらいいなと思った</li>
<li>年末年始暇だった</li>
</ul>

# 教材
### 教材①

https://www.amazon.co.jp/gp/product/B07JK33NGY/ref=ppx_yo_dt_b_d_asin_title_o04?ie=UTF8&psc=1

https://www.shoeisha.co.jp/book/detail/9784798154626


基本これを周回しまくってました。
誤植が多いので、翔泳社HPの誤植表を見た後に学習することをお勧めします。問題の難易度は本番試験とほぼ同じように思えました。また、解説も充実しているかと思います。
執筆現在、最新版がver2.0のものになっており、こちらを使って学習したので、前回の範囲差分となるES6に関しては、次の教材で対応です。

### 教材②

https://www.amazon.co.jp/gp/product/B0BL755933/ref=ppx_yo_dt_b_d_asin_title_o04?ie=UTF8&psc=1

公式認定書籍としては唯一の最新バージョン対応です。
公式が認定している教材のES6の分野を把握することで、求められているレベル感を把握できると考えました。
また、説明やコード例が充実しているので、苦手分野対策としての情報源としても購入しました。


# 勉強方法

### 10日目まで
①とりあえずスピードマスターを2周ほどやりました。
過去の別試験の教訓から、理解が浅いところは、vscodeやcodesandboxで環境作って、すぐに実機検証しました。

②また、苦手分野対策として、該当知識をNotionでページを作ってまとめて、
直前期の振り返りで使ったり、出社時の電車内で見たり見なかったりしました。

③意識していたことは、今試験はJSの仕様のかなり細かいとこまで出題される可能性が高いので、理解が不明瞭な点、書籍内のイベント一覧の項目などは、基本的に1度試すこと、Mdnのページを見るなどを意識していました。

### 20日目まで
①各項目90%以上取れてきたので、

1.模試を解いて苦手分野を把握
2.該当分野の問題を演習し直す
3.また模試を解く

を繰り返しました。ひたすらスピマスをやりこんでいました

②模試を解いて90%で安定してきたら、このタイミングで以下のことを行いました。

1.受験申し込み
2.ES6の仕様をマイナビ教材で学習

団体公認教材のES6分野を勉強することで、試験合格に求められているレベル感を把握しました。

### 直前期
20日目までにやっていたこと＋苦手対策としてまとめたページのインプットをしていました。もちろん、理解の甘い部分は実機検証をして、苦手箇所を潰していきました


# 試験当日
<ul>
<li>到着後、受験手続きなどをしました。写真取るので髭の処理忘れなど、身だしなみに注意（自戒）</li>
<li>デスクに案内されて、耳栓的なものがあれば使うこともおすすめします。他のCBT形式の試験の受験者もいることがあるので。</li>
<li>おそらく時間が余るので、「間違ったものを指定」や「○つ」選択が多い当試験では、<b>見直しは複数回することをおすすめします。</b></li>
<li>基礎的な内容が大部分を占めていましたが、中には細かい仕様の問題があったので、わからない問題は余った時間で対処できるように、番号をホワイトボードにメモしてました</li>
<li>ブラウザAPIや文法で意外と失点していたので、この辺を重点的にやっておくといいのかなと思います。全体的に出題の優先度の通りに問題が配分されている印象を受けました。</li>
<li>受験した当日に点数と合否がわかります。いいですね👍</li>
</ul>

# 感想
他にも良さげなフロントエンドの資格あれば教えていただきたいです。
VSCodeでPythonのPipenvの仮想環境から抜け出せない

# 結論
VSCodeでPythonを扱う際、自動的に仮想環境が適用されるので、別の環境を使用したい場合は、手動でインタープリタを選択する。

# はじめに
VSCode内のターミナルを使用してPythonの仮想環境であるPipenvを利用していましたが、ある時から仮想環境から抜け出せない状態になりました。
なんとか解決したので、原因と解決策をまとめます。

## 環境
- Mac OS 12.6.2
- Visual Studio Code 1.74.3
- pyenv 2.3.8
- pipenv 2022.11.30

Pythonはpyenvを利用してバージョン管理をしています。

# Pipenvとは
プロジェクトごとに仮想環境を作成、それぞれの環境でライブラリをインストールできます。複数のプロジェクトを一台のPCで同時進行する際に、お互いに環境に影響されずにすみます。
>It automatically creates and manages a virtualenv for your projects, as well as adds/removes packages from your Pipfile as you install/uninstall packages. It also generates the ever-important Pipfile.lock, which is used to produce deterministic builds.
[Pipenv: Python Dev Workflow for Humans](https://pipenv.pypa.io/en/latest/)

# 環境の変更ができない
私はVSCodeの1つのワークスペースで複数のプロジェクトを同時進行（1つは本番用のフォルダ、1つはお試し用のフォルダ、といったように）しています。
いろいろ実験をしてみたかったので、プロジェクト（フォルダ）ごとに別のPipenvの仮想環境を作成し、インストールするライブラリを別のものにしてみようと思ったのですが、一つのPipenvの仮想環境から抜け出せなくなりました。
以前は、仮想環境に入っている状態で、「exit」コマンドで環境から抜け出せていました。
```bash:ターミナル
exit
```
それが、あるときからexitコマンド打つと、VSCodeのターミナルが閉じてしまい、仮想環境から抜け出せません。

# VSCodeは自動的に仮想環境を適用する
原因を探している際、こんな記事を見つけました。
>VSCodeでは適用する仮想環境（Pythonオリジナルの設定環境含む）を選択することができますが、プロジェクトとして読み込んだフォルダの階層に仮想環境がある場合はデフォルトで仮想環境を適用してくれます。（但し、フォルダ内にpythonのファイルが含まれている場合に限ります）
[Pythonで仮想環境virtualenvを作成し、Visual Studio Codeで自動適用する開発環境を構築する](https://qiita.com/psychoroid/items/6646d45d6a019f5cf16f)

仮想環境を自動的に適用することがあることを知り、VSCodeの公式ドキュメントを読んでみると、以下の記述がありました。
>If an interpreter hasn't been specified, then the Python extension automatically selects the interpreter with the highest version in the following priority order:[Using Python environments in VS Code](https://code.visualstudio.com/docs/python/environments)

やはり自動的にインタープリタを選択するようです。では、インタープリタを手動で選べば良いでしょう。
では、そもそも現在はどのインタープリタが選択されているかですが、それはVSCodeのウィンドウ右下、ステータスバーに表示されています。
![スクリーンショット 2023-01-28 10.10.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925094/5efe94fb-38cd-ae65-17b8-ed17d0db4018.png)
現在は pipenv で application というフォルダに作成した仮想環境が動いている状態です。これを、私の場合はpyenvを利用したPython 3.9.0に切り替えたいと思います。

# VSCodeで手動でインタープリタを選択する
手動でインタープリタを選択し、自動的に別の仮想環境を適用しないようにするには、
F1 もしくは shift + command + P でコマンドパレットを開きます。
![スクリーンショット 2023-01-28 10.07.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925094/fb8eda61-989e-500d-5729-51dca7745826.png)
「Select Interpreter」を選択します。
もしくは、右下ステータスバーのインタープリタ名をクリックすると、インタープリタ選択ウィンドウが表示されます。
![スクリーンショット 2023-01-28 10.14.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925094/6ae34964-e608-8c5c-9242-00795b181cc8.png)
「ワークスペース　レベルで選択」をクリック。
![スクリーンショット 2023-01-28 10.15.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925094/76ba5b4b-5db0-cb61-be04-e5fe3caf4c0f.png)
表示された一覧の中から該当するインタープリタを選択します。私の場合はpyenvのPython 3.9.0です。

# まとめ
VSCodeを使用してPythonを扱う際は、右下のステータスバーにて、現在どのインタープリタが適用されているかを確認してみると良いみたいです。

# 参考ページ
https://qiita.com/psychoroid/items/6646d45d6a019f5cf16f

https://code.visualstudio.com/docs/python/environments
BizRobo!のスニペット～初心者あるある～

就業先では、RPAツールのBizRobo!を使用しています。

BizRobo!で開発されたロボットを改修する際、スニペット(Snippet)を作成することになり、不明点を解決するのに時間がかかりました。

今回は、現場に配属されてからBizRobo!を知った私が、BizRobo!のスニペット(Snippet)について解説します。

※間違っている箇所や不適切な表現があるかもしれませんが、その時は都度修正いたしますので、ご指摘いただけると幸いです。

## スニペット(Snippet)とは
ロボットの一連のステップを部品化する機能です。スニペットは、「.snippet」という拡張子のファイルが実態であり、ロボットファイルやタイプファイルと同じくプロジェクト内にあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955689/5292fa6e-12fc-87e6-2dc6-ce5768db8ec7.png)

Webページへのログイン処理やファイルのオープン処理、ログ出力処理など複数ロボットで同じ処理を行う場合、スニペット(Snippet)を利用することで、再度開発・テストを行う時間を省略できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955689/3b9d1d52-e359-b679-fdb3-ea29cc8d8225.png)

また、複数のロボットに同じスニペットを利用しても、1つのスニペットを編集するだけで、済みます。ただし、別プロジェクトに配置したスニペットは手動でコピーする必要があります。

Bizrobo! ナレッジベースでもスニペットについて解説していますので、下記も参考にしてください。

>[スニペット-Snippet-の活用](https://knowledge.bizrobo.com/hc/ja/articles/360041383272 "スニペット-Snippet-の活用")
>[Snippet-スニペット-を編集する際の注意事項](https://knowledge.bizrobo.com/hc/ja/articles/360027879972 "Snippet-スニペット-を編集する際の注意事項")
>[スニペットを使用する](https://knowledge.bizrobo.com/hc/ja/articles/9058379820825 "スニペットを使用する")
>引用：BizRobo! ナレッジベース


## スニペット(Snippet)における注意点
### 変数の引継ぎ
スニペットを作成する際、変数を引き継ぐことができます。
スニペットで使用する変数を引き継いでおくことで、ロボットにスニペットを適用する際、変数が自動で追加されるようになります。
もしスニペットと同じ変数がロボット内に存在すると、下記のようなエラーが発生します。スニペット内で使用している変数をロボット内で作成しないようにしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955689/873dd5c6-1290-d355-9958-e948b9d31b0b.png)

また、既に作成したロボットの改修時にスニペットを作成すると、変数引継ぎ画面がポップアップされます。（Bizrobo! version10.3.0.7）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955689/d0200c07-7ed4-2a4c-9da1-01cbc76d2457.png)

上記で一覧化される変数は、スニペットに必要な変数だけでなく、ロボットに存在する全ての変数が一覧になります。スニペットに必要ない変数まで引き継いでしまうと、別ロボットに引き継いだ際、余計なエラーを発生させたり、変数の管理が複雑になったりしますので、注意してください。

一方、スニペットに必要な変数を引き継がれていない場合、スニペット内で下記のようなエラーが発生してしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955689/69c4380e-b8ad-ff4c-fb05-fc83d4e40363.png)

上記は、該当する変数が存在しないと発生するエラーであり、この状態ではスニペットで止まってしまいます。

変数を引き継ぐ際は、スニペットで必要な変数のみ引き継ぐことを意識してください。

### スニペット編集方法
スニペットには特殊な特徴があります。
・変数はスニペットファイルからのみ編集可能
・ステップはスニペットファイルが適用されているロボットから編集可能

つまり、スニペットを編集するには、ロボットファイルとスニペットファイルを両方開く必要があります。この辺は不便さを感じるかもしれませんが、慣れていきましょう。


## 最後に
BizRobo!のスニペットについて、解説いたしました。
BizRobo!のeラーニングでスニペットについて学びましたが、開発時にスニペットを使用しようとすると、スニペットについて理解の足りなかった点がみえ、実際の現場による経験がいかに大事かが身に沁みました。

スニペットに関しては、BizRobo! ナレッジベースに情報がありますが、変数の引継ぎに関する情報がありませんでした。今回、それを補足したいと思い、記事を書きました。今回の記事を読んで、BizRobo!におけるスニペットの理解を深め、実際の現場に役立ててもらえたら幸いです。

また、今後もBizRobo!開発に役立つような記事を書いていきます。
光明は "exec bash"

## 何もない

とりあえずここに入って、と言われて。
入ってみたものの、何もない。
`ps aux` はエラーだし、 `vi` も利かないし。
矢印キー押すと `^[[A` とか出るし。

## bashだっつってんだろ。

```
# history
/bin/sh: 1: history: not found
```

ん？　`sh` なの？

chshしよ。

```
# chsh -s /bin/bash
# echo $SHELL

# history
/bin/sh: 4: history: not found
```

んん？なんで `/bin/sh` のままなんだよ……

## "exec bash"がすべてを解決してくれた

色々調べて。

```
exec bash
```

```
root@ip-x-x-x-x:/# echo $SHELL
/bin/bash
root@ip-x-x-x-x:/# history
    1  history
```

ようやくまともになった。

## 余談でしかない

`ps` は `procps`
`vi` は `vim`

を入れることでなんとかなりました。 `history` は bashのビルトインだって初めて知ったよ。
Google Cloud Platform, Cloud FunctionsでつくるLINE Bot(3)

[前回](https://qiita.com/hsmtkk/items/eef172ae7aa2bce83673)にオウム返しBotの最低限の機能を実装しました。
しかし、今の実装ではLINEプラットフォーム外からのアクセスも受け入れてしまう問題があります。
LINEプラットフォームからのアクセスだけ処理するように実装を改善します。

# 実装方針
[署名を検証する](https://developers.line.biz/ja/reference/messaging-api/#signature-validation)のとおり、以下の値が一致することを確認します。
不一致の場合には、処理を中断します。

1. HTTPヘッダー `x-line-signature` の値
1. ダイジェスト値


- ダイジェスト値 の計算式は以下のとおりです。
```
Base64(HMAC-SHA256(リクエストボディ、チャネルシークレット))
```

- LINE Developersのコンソール画面でチャネルシークレットを入手できます。
  - チャネルアクセストークンと同様に、Secret Managerに登録しておきます。

- HMAC-SHA256の計算には [pkg.go.dev/crypto/hmac](https://pkg.go.dev/crypto/hmac) を使用します。

# 実装

上の実装方針の通りコーディングするのみです。

```Go
func validate(header http.Header, reqBytes []byte) bool {
	want := []byte(header.Get("x-line-signature"))
	channelSecret := os.Getenv("CHANNEL_SECRET")
	mac := hmac.New(sha256.New, []byte(channelSecret))
	mac.Write(reqBytes)
	got := []byte(base64.StdEncoding.EncodeToString(mac.Sum(nil)))
	log.Printf("want HMAC-SHA256 %s", string(want))
	log.Printf("got  HMAC-SHA256 %s", string(got))
	return hmac.Equal(want, got)
}
```

# 動作確認

- 正常時のログ例は以下のとおりです。

```
2023/01/28 01:05:26 want HMAC-SHA256 cdcJyc6cR5lsvYLq9wCtrpzuX/YuchJsNjOk7VZEgJ4=
2023/01/28 01:05:26 got HMAC-SHA256 cdcJyc6cR5lsvYLq9wCtrpzuX/YuchJsNjOk7VZEgJ4=
```

- LINEプラットフォーム外からCloud Functionsにアクセスするとエラー終了になります。
  - チャネルシークレットがわからず、x-line-signatureに正しい値を入れられないため。

```
2023/01/28 01:01:02 invalid request
```

# ソースコード全体

ソースコード全体は以下のとおりです。
https://github.com/hsmtkk/qiita-gcp-cf-line-bot/blob/main/parrot/parrot.go

# 次回

以上でオウム返しBotの実装とセキュリティ強化は完了です。
次回はより高度な話題として、セッション管理を扱います。
一問一答完結でなく、複数回のやりとりをおこない、対話的な動作をめざします。
Google Cloud Platform, Cloud FunctionsでつくるLINE Bot(1)

Google Cloud Platform(以下GCP)のCloud FunctionsでLINEのBotを作成します。
プログラミングにはGo言語を使用します。
以下、全4回の予定です。

1. (本記事)LINE BotとCloud Functionsの作成
1. [オウム返しBot](https://qiita.com/hsmtkk/items/eef172ae7aa2bce83673)
1. [セキュリティ強化](https://qiita.com/hsmtkk/items/9dc31403ebdf1f11a0b1)
1. セッション管理(BMI計算Bot)

第1回の目標は以下の通りです。
- Cloud Functionsを作成する。
- LINE Botを作成する。
- Cloud Functions側でアクセスログの出ていることを確認する。

# Cloud Functionsを作成する

HTTPリクエストをログに出力するだけの簡単なCloud Functionsを作成します。
[Cloud Functionsのチュートリアル](https://cloud.google.com/functions/docs/create-deploy-gcloud?hl=ja#functions-prepare-environment-go)にあるものを少しカスタマイズします。

```go
package simple

import (
	"log"
	"net/http"
	"net/http/httputil"

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
)

func init() {
	functions.HTTP("simple", simple)
}

func simple(w http.ResponseWriter, r *http.Request) {
	dumpReq, err := httputil.DumpRequest(r, true)
	if err != nil {
		log.Fatalf("failed to dump HTTP request; %v", err.Error())
	}
	log.Print("dump HTTP request")
	log.Print(string(dumpReq))

	w.WriteHeader(http.StatusOK)
	if _, err := w.Write([]byte("OK")); err != nil {
		log.Fatalf("failed to write response; %v", err.Error())
	}
}
```

Cloud Functionsをデプロイすると呼び出し用のURLがわかります。

# LINE Botを作成する

[公式サイトのBot作成手順](https://developers.line.biz/ja/docs/messaging-api/building-bot/)にしたがいます。
要点を簡単に記述します。

* プロバイダーを作成する。
* `Messaging API` チャネルを作成する。
* WebhookにCloud FunctionsのURLを設定する。

![webhook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/d3312262-3966-66c8-ccca-8d80a4c082cb.png)

# Cloud Functions側でアクセスログの出ていることを確認する

LINE Developersコンソール上のQRコードを読み込み、友だちとして追加します。
適当なメッセージを送り、Cloud Functions側にログの出ることを確認できればOKです。

![cf-log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/cdcca130-1145-e66b-97c8-05f618b5a269.png)

HTTPリクエスト全体をログに出力しているので、メッセージそのものもログ中で確認できます。
以下は「こんにちは」と送信した場合のログです。

![message-event.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/bb3622ac-70e8-a68a-f8a8-be747f67b27c.png)

# ソースコード全体

ソースコード全体は以下のとおりです。
https://github.com/hsmtkk/qiita-gcp-cf-line-bot/blob/main/simple/simple.go

# 次回
[次回](https://qiita.com/hsmtkk/items/eef172ae7aa2bce83673)は受信したメッセージをそのまま返信する機能(オウム返し)を実装します。
[Docker][nginx][Wordpress]ただただnginxでWordpressを立ち上げたいだけなのにハマった話

# たかだかローカルDockerでWordPress環境立ち上げたいだけなのに一日ハマった、、、
自前のさくらのサーバーのApache&WordPress環境をDocker&nginx&Wordpressに移行したくて、ローカルで試しているのですが、結構ハマりました。
[Github:Dockerイメージ](https://github.com/otamaninja/wp_nginx_for_local/blob/main/README.md)



いろんなHP参考にして試しましたが、最終的に以下のページを参考にしました。

[Docker: Nginxを使ったWordPress開発環境の構築方法](https://keruuweb.com/docker-how-to-build-wordpress-development-environment-using-nginx/)

localhostにアクセスしますが、データベースのエラーでWordPressの設定画面が立ち上がりません。　色々調べるとMySQLがエラーで再起動を繰り返していました。

MySQLに関する問題は２点ありました。
[Mysql が立ち上がらない（パスワード設定エラー）](https://c-a-p-engineer.github.io/tech/2021/07/05/docker-mysql/)
[解決策：docker-composeのボリュームの書き方を変更](https://www.code-mogu.com/2020/12/10/docker-mysql/)

まだ、ローカルサーバー立ち上げフェーズですが、結構ハマりますね、、、

それ以外にもいろいろ問題起きたのですが、もう忘れました。　思い出したら追記します。
様々な認証方式について(Bearer,Basic,Digest,Form,OAuth)

Laravel(APIモード)でfetch APIを使ってフロントとの通信を行う際に、認証を要するアクションでAuthorization: Bearer ${token}を使って認証を行う機会があり、そもそもBearer認証とは？という疑問から今回の記事を投稿するに至りました！

参考：[【入門】認証方式とサーバーの設定をわかりやすく解説](https://hogetech.info/network/web/authentication)
参考：[トークンを利用した認証・認可 API を実装するとき Authorization: Bearer ヘッダを使っていいのか調べた](https://qiita.com/uasi/items/cfb60588daa18c2ec6f5)
参考：[一番分かりやすい OAuth の説明](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be)

## 認証の種類について

たくさんあります。

- セッション
- リクエストにトークンを含める
- ヘッダにトークンを含める
- Authorizationヘッダでの認証
- JWT認証

今回はwebサービスにおいてよく使われる**タイトルの認証方法についてまとめたいと思います。**

## Bearer（アクセストークン認証）

ログイン・パスワードでユーザー認証を行った際に、認可サーバーから発行されるアクセストークンを使って、APIのリクエスト時にAuthorizationヘッダにアクセストークンを含めて送信します。

### Bearer認証の流れ

1. クライアントがログイン時にサーバーはアクセストークンを発行
2. 発行されたトークンをクライアントのブラウザ内のsessionStrageかlocalStrageに保存
3. ページへのアクセス時にAuthorizationヘッダにトークンセットし、サーバーにRequest送信(下記コード参照)
4. サーバーはトークンを確認し、有効ならリクエストされたページをレスポンス

認証に失敗するとサーバーは401 Unauthorizedを送信する。

トークンの形式は[token68](https://wiki.suikawiki.org/n/token68#gsc.tab=0)の形式で指定することが定められています。

```php
// SessionStrageからトークンを取得し、変数に代入
const token = sessionStorage.getItem('access_token');

fetch("/api", {
      method: 'GET',
// トークンを送信
      headers: {
        Authorization: `Bearer ${token}`
      }
    })
.then.......
```

発行したトークンはクライアント側がlocalStrageかsessionStrage、サーバー側はDBにそれぞれ保存します。

## Basic認証

AuthorizationヘッダにIDとPASSをセットし、[Base64](https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d4)でエンコードした上で送信する方法。認証に成功すると認証情報がキャッシュに保存されます。

### Basic認証の流れ

1. クライアントがIDとPASSを入力
2. Base64でエンコードしてサーバーに送信
3. サーバーはデータをBase64でデコードして認証を行う
4. 認証が成功するとサーバーからクライアントに要求されたページを送信
5. ブラウザは認証状態を[キャッシュ](https://support.yahoo-net.jp/SccYjcommon/s/article/H000009988#:~:text=%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%A8%E3%81%AF%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%9F,%E3%81%AB%E3%81%AA%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82)し、キャッシュクリアされるまで認証状態が継続される

Base64はデコードすれば簡単にIDとPASSが見れるため、脆弱性を含んでいます。しかし、この問題はHTTPS通信(SSL/TLSを用いてデータを暗号化)でカバーすることが可能です。

Basic認証単体ではログアウト機能の実装ができません。後述するForm認証(後述)によって、ログアウト機能を実装することが可能です。

## Digest認証

Basic認証の脆弱性を改善した認証方式だが、HTTP通信に有効な方法であるため、HTTPS通信が主流の現代においてはBasic認証の方が利用されます。

### Digest認証の流れ

1. サーバーがランダムな文字列を生成し、クライアントに送信
2. クライアントがIDとPASSを入力
3. ランダムな文字列とPASSをAuthorizationヘッダにセットし送信
4. サーバーがデータが正しいか確認し、認証を行う

## Form認証

前提として、Form認証はHTTPの仕様にはない認証方式です。つまり、アプリケーション側で実装する方法です。

認証に成功すると、ブラウザ内のSessionにセッションIDを保存することでログイン状態を管理します。

Basic認証との違いは、[ログアウト機能](https://itpfdoc.hitachi.co.jp/manuals/3020/30203V4220/V420073.HTM)、[シングルサインオン機能](https://www.ntt.com/bizon/glossary/e-s/sso.html#:~:text=%E3%82%B7%E3%83%B3%E3%82%B0%E3%83%AB%E3%82%B5%E3%82%A4%E3%83%B3%E3%82%AA%E3%83%B3%EF%BC%88SSO%EF%BC%9ASingle,%E8%AA%8D%E8%A8%BC%E3%81%8C%E8%A1%8C%E3%82%8F%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82)が実装できる点です。

### Form認証の流れ

1. クライアントがHTMLフォーム画面にIDとPASSを入力し、サーバーへ送信(平文での送信のため、HTTPS通信が必須)
2. サーバーは内容を検証後、正しければセッション管理を開始しブラウザにセッションIDを返却
3. クライアントはセッションIDが有効な期間ログイン状態を維持することが可能
2022年に開発したアプリを振り返る

# 2022年に作成したアプリを振り返る

今更ですが、2022年に作ったアプリを振り返ろうと思います。(修論書いてたら、2022年終わってました)

## 結論

- LINEチャットでカロリー計算してくれるアプリ
- Googleカレンダーから使った時間を計算する自己分析サイト
- 法政大学のサークル一覧サイト

## LINEチャットアプリ
![9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/c8183df8-3b8b-58b8-ac6f-9801b9ad51df.jpeg)


### システム概要
システムの大まかな概要は、図のとおりです。
Line messaging apiとFlaskを使ってwebアプリケーションを作成したました。

![10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/a522dba4-fa58-a0bb-8ddd-f851a4fd9472.jpeg)


### 機能
- 食材とグラム数を入力すると、カロリーが計算される
- 1日の総摂取カロリーが計算される
- メニューを登録することで、材料を打ち込まずにカロリー計算をしてくれる
- 摂取カロリーと体重を記録し、スプレッドシートでグラフが見れる

![12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/f6822ba1-280d-51d3-15ea-2133848f33f0.jpeg)
![15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/6a03d79c-09df-e01b-6eaf-d8814ce0cf95.jpeg)


### 感想
![17.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/a902804b-089c-30ee-bd67-844f4d4f7cd8.jpeg)

ここでは詳しい開発方法などは省きますが、ダイエットを目的として作成したアプリは結論から言うと大成功。
64Kgあった体重は最小値52Kgまで落とすことになりました。

[詳しくは、こちらの記事を参照してください。](http://mentalcarediet.com/2023/01/25/9month12kg/)
[http://mentalcarediet.com/2023/01/25/9month12kg/](http://mentalcarediet.com/2023/01/25/9month12kg/)

## 自己分析サイト

### システム概要

DjangoとVueを使ったアプリケーションになります。

Google　calender　APIを使って、googleカレンダーの情報を取得し、バックエンドのpythonでカレンダー情報を整理する流れです。

![googleカレンダーから自分が使った時間を算出するアプリ【年始】.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/4599fa34-604f-8c19-5815-66b9bbe1242c.jpeg)

### 開発手順
開発手順を大まかに解説します。
1. Google calender apiの設定
2. Djangoでのユーザモデルの作成
3. CRUD機能開発
4. VueでUIを作成
5. VueとDjangoの接続(webpack_loaderを使用しました)
6. APIからデータの取得及び、vue-chartでグラフ化

### 機能
- ユーザー登録
- ログイン・ログアウト
- googleカレンダーからデータの取得
- データのグラフ化
![スクリーンショット 2022-11-03 9.52.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/ddfa4bea-9057-d23b-1275-72eab81f7cc2.png)
![スクリーンショット 2023-01-28 9.57.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/127fbc7c-b92a-e30d-a598-124a12007258.png)

### 感想
本番環境で使用できるように、Herokuを使ってデプロイしてみたのですが、googleカレンダーapi関連でエラーが起きてしまい、本番環境での使用を断念。自分の力不足です。修論書き終えた頃に、再度チャレンジしてみようと思います。

## 法政サークル一覧
![スクリーンショット 2023-01-28 9.58.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620639/a98bee43-54a1-ece0-8524-2799baee8ae9.png)


学部生時代に明学ナビというwebサイトをwordpressを用いて作成したのですが、これを法政大学verにアレンジして、Vueを使って作成してみました。

Vueの勉強の一環で作成したものなので、アプリケーションの運用は行っていません。

システムとしては、フロントにVue、データベースはFirebaseを使用しました。

### 機能

- サークル登録・編集機能
- サークル投稿機能
- ブログ記事投稿(管理者のみ投稿可能)

### 2022年を振り返った感想

2022年はフリーランスのエンジニアとして働く中で技術的なことも含めたくさんのことを学びました。

2022年学んだ技術要素

- Vue
- Firebase
- Django、Django restframe work
- Heroku,AWS
- Line、Twitter、GoogleのさまざまなAPIを触る
- Java、SpringBoot

などなど本当に色々な技術に携わることができたと思います。

2023年は4月から新卒エンジニアとして働くことが決まっているので、入社する前に興味のある技術にチャレンジしてみようと思います。

また、『毎年1個の新しい言語を習得する』という目標を掲げているのですが、2023年は『Go言語』を極めようかなと思います。

2023年学びたいこと

- ゲーム開発
- Go言語の習得
- javaを使った大規模システムに関わる

2023年も良いエンジニアライフを送れるよ、日々学び続けたいと思います！！
30歳SEが初めて転職活動をした話（Webエンジニア：フロントエンド）

最近、初めて転職活動をしました。
大変だったことや、自分の想いをまとめておきたい＆同じような境遇の人の参考になればな〜という気持ちで書いていきたいと思います。（初めて書くので読みにくいところもあるかと思いますが、ご容赦ください・・・）

## 経歴
新卒でSIやってる中小企業に就職し、そのまま在籍8年目。
いずれも自社PKGを扱う部署で以下を経験
* 部署1つめ：6年在籍
自社PKG医療機関向け業務システム
使用技術：VB.NET、C＃、Java、C、SQL（Oracle、Postgres）、Linux（RedHat、CentOS）

クラサバ構成のため導入顧客ごとにひたすらサーバ構築、PKGカスタマイズ開発、保守運用、カスタマーサポートまでなんでもやってました。正直開発よりも導入支援や保守業務の方が多く、開発案件はたまにあるけど定時内は他の作業で埋まってしまい、定時後から開発スタート・・・みたいなヤバイ労働環境でした。忙しくて大変でした。

* 部署2つめ：2年在籍
医療機関向け業務支援システム
使用技術：Java(Wicket)/HTML/CSS/jQuery/SQL(Postgresql)/Linux(CentOS)

2年前ぐらいにクラウドサービスで自社PKGを企画開発している部署に異動しました。
変わらずPKGカスタマイズ開発、運用保守、カスタマーサポートまで担当していましたが、クラウド構成で、顧客の数もそこまで多くなかったので比較的開発業務に集中して取り組んでいました。
また、旧部署より労働環境が大幅改善＆使用技術が一新したこともあり、毎日のように技術のキャッチアップや勉強に精を出していました。

## 転職のきっかけ
現職では保守的なエンジニアが多く、先人が作ったシステムを同じ技術のまま使い続ける傾向がありました。
世の中のWebエンジニアの情報発信で勉強しているうちに、自分はもっと色んな技術の選択肢を持てるエンジニアを目指したくなり、環境を変えてみようと思いました。


組織としても全体的にあまり新しいやり方を好まない傾向があり、売上も先人たちの顧客やソリューションを延命させているような状況だったので、そういったことに疑問を持ち始めていたことも転職に踏み切ったきっかけの1つです。中堅社員あるあるですね・・・

## 転職先企業の選択軸
元々UIデザインに興味があって個人的に勉強していたこともあり、デザインを形にできるフロントエンドエンジニアとしての専門性を高めたいな〜と思いながら求人を探していました。

中でも下記3点を意識しながら企業選びをしていました。
* フロントエンド開発ができて、新しい技術を（必要に応じて）積極的に採用している
* UI/UXデザインを推進している
* 組織としての意思決定が合理的に行われていそう（風通しが良さそう）



## 結果
応募企業：約20社
書類通過：5社
面接：4社
内定：2社

思っていた以上になかなかハードな結果でした。
※ 転職エージェントを利用
## 考察
* 敗因
  * 自分の経験と企業側のニーズのミスマッチ
  
具体的には、自分がこれからやりたい「フロントエンドエンジニア（モダンな技術を使用）」という要件で応募していたことですね。
そもそも今の業務では使っているフレームワークの都合上Java中心のコーディングが多く、フロントエンドはJavaScript（jQuery）でチャチャッと調整する程度だったので「ReactとかVueの業務経験ないのでダメ」「開発経験が浅い」みたいな理由でバンバン落とされました。個人的にReactは勉強していたのですが、公開可能なポートフォリオまではしっかり作れていなかったので当然だなーと。
特に大手の自社Webサービスを扱う企業さんはフロントエンド、バックエンド、インフラはきっちり役割分担されているので求められる専門性や経験年数は高かったと感じました。

* 成功要因
  * サーバ構築、バックエンド、フロントエンドまで幅広い経験があった
  * 資格取得を積極的に行っていた

「フロントエンドだけじゃなくて必要に応じてバックエンドもやってほしいなぁ」と思っている企業さんとマッチできたため、今までの経験も活かしつつ、自分のやりたいことにも取り組める活路を見出せました。また、たまたまかもしれませんが、内定をいただけた企業さんはみんな「フレームワークは所詮フレームワークだから。これからもどんどん変わるし、基本的な技術がある人なら、勉強すれば問題なく習得できる」といったスタンスで許容してくれていました。また、資格取得についても難易度に関わらずコンスタントに取得していたため、やる気だけでなくちゃんとやる人だ、と判断していただくために役立ちました。

※　所持資格：応用情報技術者、Webデザイン技能士（2級、3級）、HTMLプロフェッショナル認定試験Level.1

## 感想
自分の市場価値を改めて知ることができ、転職活動やって本当によかったと思います。
弱みは気付きやすいですが、意外と自分の強みって分からないものです（自分の中で当たり前にやっている部分が多いので）。今回転職エージェントさんに恵まれ、この辺りのことは職務経歴をみながら一緒に分析していただけたので助かりました。
また、新卒や第二新卒に比べると、給与面も気になるお年頃だとは思いますが、正直なところ、一般職プログラマーとして転職するとなると給与大幅UPはあまり見込めないかもしれません。評価制度や昇級制度についてはしっかり確認しておくと良いかもです。

長くなりましたが、同じように考えている誰かの参考になればと思います！
ありがとうございました〜
FlaskFormで、formを動的に生成する

FlaskFormはイロイロ便利ではありますが、もう少し柔軟に使えないかなぁ・・・と、お悩みのあなたに。

普通は、こんな感じで事前に定義しておきます。
~~~
from flask_wtf import FlaskForm
from wtforms import StringField

class EventForm(FlaskForm):
    name = StringField('Event Title')

form = EventForm()
~~~

これを動的に生成する方法を考えてみました。

メリット
・Viewを共通化できます。
・Formクラスの生成をカスタマイズできます。
・FlaskFormの利便性を、そのまま享受できます。

まず、設定情報を作ります。
~~~
form_conf = {
    'Event': {
        'name': StringField('Event Title')
    }
}
~~~

次にクラスを定義します。
~~~
class ExForm:
    def __new__(cls, kind):
        field_dict = model_conf[kind]
        return type(kind, (FlaskForm,), field_dict)
~~~

そして、formを生成します。
~~~
kind = 'Event'
F = ExForm(kind)
form = F()
~~~

あとは、お好きにどうぞ。
Salesforce Scratch Org をもちいたコマンド一巡メモ

# この手順のデイリー部分の抜粋

- [Salesforce Scratch Org 作業のデイリー・コマンドライン](https://qiita.com/igapyon/items/509b706fc2da0a8c0b5b)

# Salesforce `Scratch Org`（スクラッチ組織）をもちいた手順の一巡

Salesforce `Scratch Org`（スクラッチ組織）をもちいた手順の一巡について、コマンドの記述手間軽減のためにそのまま再生可能な状態のコマンドラインをメモします。


- `Dev Hub` から `Scratch Org` を作成、プッシュ、オープン、プル、削除までの一巡が対象です
- `Scratch Org` ベースの開発では、この一連手順を 1日のうちに何度も繰り返すことでしょう
- この一連は `Salesforce DX` と呼ばれるものの一部とも見なすことができます。

## 前提

- Salesforce CLI（sfdx）ベース開発の知見一式がり、導入済みであること
- Dev Hub 有効な組織を使用可能であること
- sfdx 用の Salesforce プロジェクトの設定が実施済みのフォルダ一式がありソースコード管理に格納済みであること

## GitLab から設定済みプロジェクトを git clone

- (わたしの場合は) GitLab から設定済みプロジェクトを `git clone` で取得します。

```
git clone git@gitlab.com:rsv...uct-pkg.git
```

- 勢いで実装するなら `/tmp` フォルダで作業してもよいかも (保証外)
- `/tmp/202301281415` 的なフォルダを都度作成して作業しても良いかも (スリリングになります)

## Dev Hub が有効な組織にログイン

Dev Hub が有効な組織にログインします。

```
sfdx auth:web:login -d -a DevHub
```

- Dev Hub 有効組織での作業となります。(わたしの場合は PBO 組織も該当しこれが活躍)

## Scratch Org を作成

`myScratch1` という名称で Scratch Org (スクラッチ組織) を作成します。（こまめに git 作業フォルダおよび Scratch Org 作成するので、実は名称は何か固定のものでもワークする）

```
sfdx force:org:create -f config/project-scratch-def.json --durationdays 1 -s -a myScratch1
```

- Scratch Org 作成は少し待たされます
    - とはいえ Developer Sandbox に比べたら爆速
- このコマンド例では Scratch Org は1日経過すると勝手に消えちゃいます

## Scratch Org にソースをプッシュ

いま作成した Scratch Org に手元のソースをプッシュします。

```
sfdx force:source:push -u myScratch1
```

- 正常終了を待ちます。
- 調整済みプロジェクトであればここではエラーは発生しないはずです。

## Scratch Org を Web ブラウザで開く

プッシュが成功したことを確認するために Scratch Org を Web ブラウザで開きます。

```
sfdx force:org:open -u myScratch1
```

- わたしの場合ここで権限セットをユーザに割り当てなどします。

## 必要に応じて開発作業

必要に応じて開発作業をおこないます。


## 変更作業を実施した場合は Org からプル

何かしら変更作業を実施した場合には Org からプルしてソースを取得します。

```
sfdx force:source:pull -u myScratch1
```
 

## git作業

ブランチ作成などして開発成果のソースを GitLab に送り込みます。

## 使用後の Scratch Org の削除

使い終わった Scratch Org は すみやかに削除するようにします。

```
sfdx force:org:delete -p -u myScratch1
```

- 削除忘れ Scratch Org の数が増えると、新規 Scratch Org 作成ができなくなる場合があります
    - その場合は Dev Hub に直接ログイン操作して もはや使用が終わった Scratch Org を削除操作するようにします。

## まとめメモ


- Scratch Org ベース開発の場合、git リポジトリが作業の中心になり、Scratch Org は使い捨てになります。
    - Scratch Org は最悪でも毎日捨て、1日のうちに数回は捨てて作成くらいのペースが好ましい開発ペースっぽいです
- 小ぶりなアプリ開発ではすこぶる快適な環境っぽいです
- 従来型開発とブレンドして、うまく生産性を向上したいです
- Scratch Org 開発は パッケージ作成や AppExchange アプリ開発などでも役立つことでしょう

# この手順のデイリー部分の抜粋

- [Salesforce Scratch Org 作業のデイリー・コマンドライン](https://qiita.com/igapyon/items/509b706fc2da0a8c0b5b)
SAWSessionService認証エラーのけん

<h2>エラー内容</h2>

Javaアプリを実行してorable BIのカタログ作成検証をしようとしたら、
以下のエラーが発生しちゃいました。
<b>The specified user credentials could not be authenticate.
認証が失敗しました。ユーザーspecified userが拒否されました。</b>

<h2>対応方法</h2>
まず、oracleサポートでエラーの件について、検索すると以下の原因可能性が述べられていました。

・OracleSystemUserアカウントがアイデンティティ・ストアの認証プロバイダに登録されない
・MyRealmにOracleSystemUserが作成されていない

しかし、上のは全て正常に設定されていたし、エラーログに設定されていたLogin Userは<anonymous>だったため、
関係ないように見えました。

それで、一応 Java → soapApi の間に正しいIDが設定されず送信されていると思い、ソースを追いかけてみたら、
以下のソースを見かけました。
```
SAWsessionServiceSoap.logon(ID, PASSWORD)
```

アカウントをBIログインユーザーにしたら、成功！
無事にJavaアプリからBIカタログ作成まで完了しまいた。
Azure Repos のブランチポリシーを Azure CLI で設定してみた

デフォルトブランチに直接プッシュするのを禁止して、別のブランチからプルリクエストを経てデフォルトブランチにマージする、という開発チームのルールがあったとします。これを master とか main とかに設定しているデフォルトブランチ共通のブランチポリシーを Azure Repos のプロジェクトに対して Azure CLI で設定してみます。

## 現状のブランチポリシーの確認

ポリシーは設定されておらず、JSON の中身は空です。

```bash:bash
$ az repos policy list \
  --org https://dev.azure.com/mnrsdev/ \
  --project mnrror
[]
```

## ブランチポリシー JSON を作成

type.id は "Minimum number of reviewers" の ID です。

```json:branch-policy.json
{
  "isEnabled": true,
  "isBlocking": true,
  "type": {
    "id": "fa4e907d-c16b-4a4c-9dfa-4906e5d171dd"
  },
  "settings": {
    "minimumApproverCount": 1,
    "creatorVoteCounts": true,
    "scope": [
      {
        "repositoryId": null,
        "refName": null,
        "matchKind": "DefaultBranch"
      }
    ]
  }
}
```

## ブランチポリシーを登録

```bash:bash
$ az repos policy create \
  --org https://dev.azure.com/mnrsdev/ \
  --project mnrror \
  --config branch-policy.json
```

## ブランチポリシーの設定を確認

デフォルトブランチのブランチポリシーが作成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/35775adc-f9a6-972b-727d-50f106567242.png)

最低一人以上のレビューを経ないとデフォルトブランチにマージできなくなりました。

またプルリクエストした人とレビューする人が同じでも良いように "Allow requestors to approve their own changes" をオンにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675885/b3a53553-bde2-10ff-545a-d4c0139267c3.png)

## 参考

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/branch-policies?view=azure-devops&tabs=azure-devops-cli

https://learn.microsoft.com/ja-jp/rest/api/azure/devops/policy/configurations/create?view=azure-devops-rest-7.0&tabs=HTTP#examples
CLIP STUDIO PAINTでレイヤージャンプ

# 概要

例えば『髪の色を調整したい』『後から塗り漏れ見つけた』といった時に、『目的のレイヤーを探して移動』するのが手間。
特に色付けまで作業が入るとレイヤー数がそれなりに多くなるので面倒。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/9d512b3d-ffee-7c6b-617b-237c602dcac2.png)

サクッとレイヤージャンプしたい。

# レイヤー選択

## ツール

操作ツールに『レイヤー選択』というものがある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/62f2e725-347e-2a15-37bc-22afd52a2404.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/42cb2940-45a3-3f95-5521-d66fe8bbead1.png)
髪をクリックすると髪の塗りレイヤーにパッと移動できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/7a107fcc-af61-f1ce-d74a-85df8e4fef96.png)
クリックした箇所でレイヤーが重なっている場合は、一番上のレイヤーに移動する。
（例えば『塗りレイヤー』の上に『線レイヤー』が重なっていたら、上に位置している『線レイヤー』の方に移動）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/cdaf06a3-9818-6614-29e5-fdc47c02ec27.png)

## キーボードショートカット

```Shift + Ctrl``` を押している間 **だけ** 『レイヤー選択』ツールになる。
キー離せば元のツールに戻る。

ツールを切り替える手間も無く、サクッとレイヤージャンプができる。

# 参考サイトさん

https://www.ipentec.com/document/clip-studio-paint-select-layer-at-click-point

# バージョン

Windows 10 Home 22H2 19045.2486
CLIP STUDIO PAINT PRO（64ビット） 1.13.1 202301171700
TensorFlowのインストール(pip)失敗でハマった話(Could not find a version that satisfies the requirement)

# 1.この記事の内容

長らく使っていなかった``Dockerfile``を使ってDocker Imageをビルドしなおす機会があり，この環境でTensorFlowの``pip install``が失敗する問題に遭遇しました．  
解決まで結構ハマってしまったのですが，初歩的なミスだったので自分への戒めも兼ねて，書き留めることにしました．

結論だけ先に述べておくと，**インストール対象のパッケージがサポートしているPythonのバージョンを使いましょう**，ということです．

# 2.エラー内容

「TensorFlow version 2.8.0rc0の要件を満たしていません」といった内容のエラーです．

```
ERROR: Could not find a version that satisfies the requirement tensorflow==2.8.0rc0 (from versions: none)
ERROR: No matching distribution found for tensorflow==2.8.0rc0
```

# 3.原因

``Dockerfile``のベースイメージに，``python:3``を設定していたことが原因でした．  

```
FROM python:3
(以下略)
```

この設定では，Pythonの最新バージョンが引き込まれるわけですが，本記事の執筆時点では``Python 3.11.1``となります．  
一方，TensorFlow version 2.8.0rc0では対応するPythonバージョンは[3.7, 3.8, 3.9, 3.10](https://pypi.org/project/tensorflow/2.8.0rc0/)なので，ここで不整合が起きていました．

# 4.対策

``Dockerfile``のベースイメージに設定するPythonバージョンを``python:3.10.9``とすることで解決しました．

```diff
-FROM python:3
+FROM python:3.10.9
```

# 5.さいごに

今回はTensorFlowでこの問題に遭遇しましたが，他のPythonパッケージでも生じうる問題なので，バージョン指定には気を付けないといけないな，と改めて思いました．

# 6.関連リンク

* [PyPI](https://pypi.org/)