OmniAICreator/Qiita-1.07M huggingface dataset:
Pritunlクライアント使ってVPN接続 for macOS

## 概要
Pritunlクライアントを使って、自宅から勤務先VPNへ接続する方法を
社内のmacユーザー向けに書いた手順です。


## 環境
- MacBook Pro(Intelプロセッサ)
- macOS Catalina 10.15.7

## 事前に用意
- MFAアプリ
- PINコード
- プロファイル
- プロファイルインポート用URI

## MFA設定

### スマートフォンにMFAアプリをインストール

- Google Authenticator

    [App Store](https://apps.apple.com/jp/app/google-authenticator/id388497605)

    [Google Play](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=ja)

または

- Twilio Authy

    [App Store](https://apps.apple.com/jp/app/twilio-authy/id494168017)

    [Google Play](https://play.google.com/store/apps/details?id=com.authy.authy&hl=ja&gl=US)

など。

インストールしたMFAアプリを開き、送付されたQRコード読み込み、アカウントを追加。

設定完了後、認証コード(数字)がアプリ上に表示される状態にしておく。

## Pritunlクライアント

### ダウンロード

以下サイトへアクセス

[https://client.pritunl.com/](https://client.pritunl.com/)

Topページの下の方に「macOS Intel」の項目があるので

「Download Intel Pkg」ボタンからダウンロード。

### インストール

保存した「Pritunl.pkg.zip」(約95MB、インストールには230MB程度必要）を展開する。

展開された「Pritunl.pkg」をダブルクリック。

インストーラが起動し、インストールが始まる。

インストール完了後、Finderなどから「Pritunl」で検索して、アプリを起動する。

## Pritunlアプリの設定

### プロファイルをURIで読み込む

アプリ画面右下にある「Import Profile URI」（青ボタン）をクリック。

以下の様なURIを入力する。
（管理者などから送付されているor自分で準備）

```
pritunl://vpn.noah2.com/ku/xxxxxxxxx
```

**※ インポート実行後、エラーが発生する場合は以下の方法を試す**

### プロファイルを直接読み込む

アプリ画面左下にある「Import Profile」（緑ボタン） をクリック。

プロファイルを指定し、「開く」でインポートを実行する。
（管理者などから送付されているor自分で準備）

### プロファイルが読み込まれた状態

![Pritunl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681323/37de6da1-b237-61ed-c6f4-8176da849e57.png)


## VPN接続

### VPNサーバへ接続

アプリ画面右上のメニュー(バーガーメニュー)をクリック。

「Connect」を選択。

入力欄に、**PINコード(数字)** を入力し、OKをクリック。

続けて「Enter OTP Code」と入力欄に出たら

MFAアプリを開き、アプリに表示されている**認証コード(数字)**を入力し、OKをクリック。

接続が成功すると以下の様な画面になる。

![Connect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681323/6328d8c6-75c1-b1a0-7b38-6f416a1ea7b5.png)


### 接続を終了

アプリ画面右上のメニュー(バーガーメニュー)をクリック、「Disconnect」を クリック。

![Disconnect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681323/79636cab-f8e6-ee0d-6067-16f82e761184.png)


### 再接続する(初回以降の接続)

上記「VPNサーバへ接続」ステップと同様に

アプリメニューから「Connect」

→ PINコード

→ MFAアプリの認証コード

と入力する事で再接続が可能。

## VPN接続状況を確認（オプション）

### IPアドレスを確認

VPN接続下での自分のIPアドレスを確認します。

普段使用しているWebブラウザで、以下の様なサイトへアクセス。

[https://www.cman.jp/network/support/go_access.cgi](https://www.cman.jp/network/support/go_access.cgi)

表示されるIPアドレスが、普段のものから変わっているかを確認する。

### スピードテスト

Webブラウザで以下へアクセス

[https://www.google.com/search?q=スピードテスト](https://www.google.com/search?q=%E3%82%B9%E3%83%94%E3%83%BC%E3%83%89%E3%83%86%E3%82%B9%E3%83%88)

最初の項目に表示される「速度テストを実行」をクリックすると、現在使用している回線スピードを計測出来る。

- VPN接続あり（例

```
76.3Mbps（ダウンロード）
57.3Mbps（アップロード）
レイテンシ: 15 ミリ秒
```

- VPN接続無し（例

```
92.8Mbps（ダウンロード）
85.6Mbps（アップロード）
レイテンシ: 12 ミリ秒
```

VPNへ接続した状態とVPNを使っていない普段の状態の両方でテストを行うと良い。

ただし、その時の様々な状況(回線、サーバ、クライアント側など）でかなり結果が左右されるのであくまで参考程度に。

---
ssh接続した先の端末のdockerへssh接続するssh.configの書き方

```
Host EC2
    HostName {IP_ADRESS}
    User ubuntu
    IdentityFile {EC2 rsa path}
    CheckHostIP no
    ControlMaster auto
    ControlPersist  600
    IdentitiesOnly yes
    ForwardAgent yes
    RemoteForward 22 127.0.0.1:22
    ServerAliveInterval 60
    ServerAliveCountMax 3
Host EC2_docker
    HostName 127.0.0.1
    User root
    Port {docker ssh port}
    IdentityFile {EC2 docker's rsa path}
    ProxyCommand ssh -W %h:%p EC2
```

windowsで設定するとき、
多段sshのopensshのバージョンによっては

“posix_spawn: No such file or directory”

というエラーが出るのでその時はgit for windowsが入っていれば以下を設定

```
    ProxyCommand C:\Windows\System32\OpenSSH\ssh.exe -W %h:%p EC2
```
参考サイト
https://teratail.com/questions/212441
NVMe SSDが認識されないときはBIOSを更新してみよ

[WD Blue SN550 NVMe WDS100T2B0C](https://bbs.kakaku.com/bbs/K0001220483/#tab)を買ったのですが、何をどう頑張ってもWindows 10がインストールされなかったり、インストールされてもそこかれ起動できなかったので、BIOSを更新したらWindows 10がインストールでき、起動もできるようになりました。

なお、Windows 10とUbuntu 18.04のDual Bootにしたのですが、Ubuntuの後にWindowsを入れる形になったので、インストール完了後にちょっと一工夫が必要です。こちらの[サイト](https://www.techsolveprac.com/install-windows-after-ubuntu-dual)を参考にしました。とくにStep3が重要です。

また、Windowsはインストール後は、Windows Boot ManagerからWindowsを起動します。自分の場合は、WindowsのパーティションはBIOS画面からでは見えませんでした。上記サイトでboot-repairした後は、Ubuntuを起動するときに、システムが選択できるようになります。しかし、やはりWindows Boot Managerしか見えませんので、やはりWBMから起動するようにします。
【Python】複数のフォルダを全てZIP形式に圧縮・解凍する方法

![Title.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695597/667b0a46-c3be-7f9d-141e-1906f7639def.gif)

# はじめに
カレントディレクトリにあるフォルダを全てをZIP形式に圧縮または全てのZIPフォルダを解凍するコードを共有して残したいと思ったので記事にしました。

# 実装
Python 3.9.1
### ZIP形式にフォルダを圧縮
以下のコードを実行することでカレントディレクトリにあるフォルダを全てZIP形式に圧縮することができます。

```python:compress_ZIP_folder.py
from shutil import make_archive
import os

path = './'

files = os.listdir(path)
folders_list = [f for f in files if os.path.isdir(os.path.join(path, f))]

# フォルダ内のZIPフォルダを全て圧縮する
for folder in  folders_list:
    make_archive(folder, 'zip', root_dir=folder)
```

### ZIPフォルダを解凍
以下のコードを実行することでカレントディレクトリにあるZIPフォルダを全て解凍することができます。

```python:unzip_the_ZIP_folder.py
from shutil import unpack_archive
import glob

path = './*.zip'

zip_folders_list = glob.glob(path)

# フォルダ内のZIPフォルダを全て解凍する
for folder in zip_folders_list:
    unpack_archive(folder, format='zip')
```

# 終わりに
複数のフォルダを圧縮・解凍する機会はなかなかないと思いますが、作業効率化にお役に立てれば嬉しいです。
Lightning環境のグローバル検索用URLのハック

classic環境では以下のように簡単にURLハックできたみたいです。

```
https://xxxx--lightning.my.salesforce.com/_ui/search/ui/UnifiedSearchResults?sen=ka&sen=003&str=[call.cli]&initialViewMode=summary&searchAll=true
```

では、Lightning環境ではどうでしょうか。ちょうど質問がありましたので調べてみると
[url strings appearing in classic rather than lightning](https://trailblazers.salesforce.com/answers?id=9064S000000DXj8QAG)

そのそもURLが違います(当然かな）、それとBase64エンコードする必要があるみたいです。

+ [ライトニング グローバル検索 URL](https://developer.salesforce.com/forums/?id=9060G000000UZky)
+ [[Lightning] URL for Global Search without Base64 encoding](https://trailblazer.salesforce.com/ideaView?id=0873A0000015BJJQA2)


回避策を頼りにコンポーネントを作ってみました。
質問者の検索条件と同じようにしてみます。

以下のコンポーネントでLightningのグルーバル検索用URLが生成されました。

コンポーネント

```
<aura:component implements="force:appHostable" access="global">
        <!-- ここに検索したい単語をセットする -->
	<aura:attribute name="search" type="String" default="call.cli"/>
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <lightning:layout horizontalAlign="spread" multipleRows="true">
        <lightning:layoutItem padding="around-xx-small" flexibility="grow" size="12" mediumDeviceSize="12" largeDeviceSize="12"> 
            <div class="slds-box slds-box_xx-small slds-text-align_left slds-m-around_xx-small slds-theme_default">                            
                <lightning:layout verticalAlign="left" multipleRows="true">
                    
                    
    				Redirecting to search results for {!v.search}
                    
                </lightning:layout>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    
</aura:component>
```

コントローラ

```
({
	doInit : function(component, event, helper) {
        // Grab search query from URL
        var searchQuery = component.get("v.search");
        
        // Convert search query into SF expected format
        // "type":"TOP_RESULTS",,"id":"Product2","entity":"Product2"
        //取引先責任者だけを指定。実際には "name":"Contact"だけでもよさそうな感じ
        var stringToEncode = 
            '{"componentDef":"forceSearch:search","attributes":{"term":"'+ 
            searchQuery + '","scopeMap":{"labelPlural":"Contacts","entity":"Contact","name":"Contact" },"context":{"disableSpellCorrection":false,"SEARCH_ACTIVITY":{"term":"'+
            searchQuery + '"}}}}';
        var encodedString = btoa(stringToEncode);
        
        // Redirect user (this only works from Lightning App, won't work from components)
        window.location = "/one/one.app?source=alohaHeader#" + encodedString;

		
	}
})
```
bulma componet編

<dl>
<dt><a href="https://bulma.io/documentation/components/breadcrumb/">class="breadcrumb"</a>
<dd>俗にいうパンくずリスト(〇〇/××/△△みたいにスラッシュで区切られてるやつ)。自分のwebサイトを作ったことがないのでパンくずリスト自体よく知らないがどっかで聞いたことがある。現在ページを表示させたりできるのだろう。home/user/yukky　みたいに。使う時が来たら追記する。

<dt><a href="https://bulma.io/documentation/components/card/">class="card"</a>
<dd><em>The card component comprises several elements that you can mix and match</em><br>
色んな要素を組み合わせて一つのカードを作るよ。公式にわかりやすい具体例があるのでそれ見るのが早い。

<dt><a href="https://bulma.io/documentation/components/dropdown/">class="dropdown"</a><br>
<dd>dropdown これがメインの容器<br>
dropdown-trigger これがトリガー。押すと一覧が出てくる。<br>
dropdown-menu トリガーを押すと出てくる一覧の容器<br>
dropdown-content これも容器。menuとの違いがよくわからない。サンプルではdropdown-menuの次に書かいてるので書かなきゃいけない感じかな？<br>
dropdown-item これが一覧一つ一つのやつ。aタグがdivタグにつけろよって書いてある<br>
dropdown-divider これは一覧を区切ってくれる線。

<dt><a href="https://bulma.io/documentation/components/menu/">class="menu"</a>
<dd>縦型のメニューを作れる。要素はシンプル。<br>
menu これがメインの容器 <br>
menu-label これがラベル。pタグにクラスでつける<br>
menu-list これが内容。ulタグにクラスでつけて中にliタグを並べれば完成。

<dt><a href="https://bulma.io/documentation/components/modal/">class="modal"</a>
<dd>
modal これがメインの容器 <br>
modal-background これを入れとくと×ボタンじゃなくて画面のどこかをタッチするだけで閉じれるようになる。みたいな感じかな？<br>
modal-content これが内容。縦横すべて中央ぞろえにしてくれる。<br>
modal-close ×ボタン

<dt><a href="https://bulma.io/documentation/components/navbar/">class="navbar"</a>
<dd>画像、リンク、ボタン、ドロップダウンを横に表示できる。<br>
navbar これがメインの容器 <br>
navbar-brand 常に表示されるやつ。ロゴとかにしとけって書いてある。<br>
navbar-burger ハンバーガーリストを作ってくれる。空のスパンタグを3つ入れろって書いてある。<br>
navbar-menu navbar-brandのとなりに表示されるやつの容器。画面が小さいと非表示になるから消したくないならis-activをつけろって書いてある。<br>
navbar-start 左側に追加されてく容器。navbar-menuの子要素<br>
navbar-end 右側に追加されてく容器。navbar-menuの子要素<br>
navbar-item 実際に表示したい内容を入れるところ。上二つのさらに子要素

<dt><a href="https://bulma.io/documentation/components/pagination/">class="pagination"</a>










　　
</dl>
初心者向けunlessの使い方

今日はunlessの使い方を学習しました。
使い方はifの全く逆です。

```ruby

if a + b > 0
  puts = "0より大きいです"
end
```

これと同じ処理をunlessメソッドを使って処理します。

```ruby

unless a + b <= 0
  puts = "0より大きいです"
end
```
ここで気をつけて欲しいのは"<"ではなく"<="を使用することです。
"<"を利用すると０も含まれるので"<="としましょう。
go-cmpでprotobuf定義の構造体をdiff/equalするときにpanicになる時の対処

ハマったのでメモ

### 助けられた
大変参考になりました。ありがとうございます :pray:
https://blog.rokiyama.dev/2020/09/11/go-cmp-diff-protobuf/

### 状況
golangでprotobuf+gRPCなサーバーサイドプログラムのテストを書いてる時に発症した。
テストでは、テスト対象の関数に渡す値と、期待される戻り値を最初に定義しておき、その2つの値（入・出）をgo-cmpで比較する方式を取っていた。👆のリンクではdiffを取っている例だけど、私の場合はEqualで再現した。

```bash
panic: cannot handle unexported field at {*package.StructName}.ChildStructName.state:
        "github.com/hoge/mypb/go/package/v1".Struct
```
結局protogenで生成したprotobuf messageのStruct内において、外部に出てない自動生成フィールド(小文字で定義されたもの)を比較できないことによって発生している模様。

```go
type Name struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Id           uint32        `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
```
panicにも出てるように、この型のstateフィールドが引っかかってるらしい。

### 対策1
ブログにたどり着き、若干状況違う中で下記の通りテストを書き直したらpanicしなくなった

```go
import(
	"google.golang.org/protobuf/testing/protocmp" // 追加でimportしておく
)

if !cmp.Equal(res, expectedResponse, opts, protocmp.Transform()) { // Equalに渡すパラメータに protocmp.Transform() を追加
	t.Fatalf("got %v want %v", res, expectedResponse)
}
```

### 対策2
go-cmpのオプション `IgnoreUnexported` を活用する
名の通り外に出てないフィールドを無視するための関数が用意されている

```go
opts := cmpopts.IgnoreUnexported(package.ParentStruct{}, package.RelatedStruct1{}, package.RelatedStruct2{})
if !cmp.Equal(res, expectedResponse, opts) {
	t.Fatalf("got %v want %v", res, expectedResponse)
}
```
比較したいstructにおいて、unexportedなfieldが入っているものを全て渡しておくと良い。protocmp.Transform()に比べて、必要な分だけ無視できる良さがあるが関連するstructが多くなると列挙するのが面倒になってくる
【VSCode】日本語丸数字の見栄えが悪いので、等幅フォントを設定する（Windows端末）

#概要
Visual Studio Codeのデフォルト表示だと、丸数字が等幅でないので、見づらい。
Visual Studio Codeに等幅フォントを設定して見栄えを改善した際の手順を記載しています。

#環境
Windows 10 Pro(1909)
VS Code 日本語化済み

#前提条件
Windows 10 October 2018 Update以降であること

# 設定手順
- [メニュー]の[ファイル] - [ユーザー設定] - [設定]より設定画面を開きます。
- Editor:Font Familyを以下変更します。
    - 既存の設定フォントの前に、`'BIZ UDゴシック',` を追記します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235774/643b3adb-4bb0-b91e-81c6-b071e7ffaced.png)

#デフォルト表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235774/bdc71f6c-02ab-3d92-f8c8-bd0bd61b4bc2.png)

#設定変更後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235774/dbe766f5-a9e1-78c9-7cb5-974ebade75b5.png)
Pythonで学ぶアルゴリズム　第27弾：文字列探索の力任せ法

# #Pythonで学ぶアルゴリズム< 文字列探索の力任せ法 >

##はじめに
基本的なアルゴリズムをPythonで実装し，アルゴリズムの理解を深める．
その第27弾として文字列探索の力任せ法を扱う．

##文字列探索の力任せ法
前から順に一致する文字列を探すという非常に単純な方法．ここでは，検索対象を「テキスト」，見つけたい文字列を「パターン」と呼ぶこととする．例えば，'she sells sea shells'というテキストの中で，'sea'というパターンが最初に登場する位置を探すことを想定する．そのイメージ図を次に示す．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/7b6c129d-46d8-164c-5c1b-114014be5292.png)
上図は少し省略して描いている．本当は，一致しなかったときには検索開始位置を１つ文字分ずつずらしていく．ここでは，イメージしやすいように，１文字目で不一致となる部分を省略している．上図からも分かるとおり，見つけ次第終了，見つけるまではひたすらずらしては探してという操作を繰り返す．文字通り，力任せ法である．

##実装
上の説明に従って，Pythonでの実装を行う．以下にソースコードとそのときの出力を示す．

#####ソースコード
```python:search_string.py
"""
2021/02/01
@Yuya Shimizu

文字探索の力任せ法
"""

def search_string(text, pattern):
    for i in range(len(text)):
        match = True    #patternと一致すると仮定

        for j in range(len(pattern)):
            #patternを順に1文字ずつ比較する
            if text[i + j] != pattern[j]:
                match = False   #不一致
                break   #探索箇所を次に移行するため，これ以上の探索はしない

        if match:   #すべて一致した ⇒ 発見 ⇒ 探索終了
            return i


if __name__ == '__main__':

    TEXT = 'she sells sea shells'
    PATTERN = 'sea'

    where = search_string(TEXT, PATTERN)

    print(f"{where}\nthat is, you can find {PATTERN} by running 'TEXT[{where}: {where}+len(PATTERN)]'")
```

#####出力
```
10
that is, you can find sea by running 'TEXT[10: 10+len(PATTERN)]'
```

探索の結果，テキストのにおけるパターンの開始位置を出力し，その結果からどのようにしてそのパターンをテキストにおいてアクセスできるかというのも出力として表示させてみた．実際にその指示に従って，``print()``などで表示させれば，パターンが得られることが確認できる．

##注意
テキストが今回のようなサイズであれば一瞬で処理は終了するが，長いテキストになると長時間かかることは原理から容易にわかる．

##感想
今回は非常に単純であったが，注意にもあるように長いテキストだと大変なようだ．しかしながら，このような探索をしたいときというのは長いテキストから何かを見つけたいときが多いような気がする．もう少し工夫して高速にした方法は次回学ぶため，非常に楽しみである．また，参考文献では，テキストとターゲットを``list()``を使ってリスト化していたが，文字列のままでも大丈夫であった．ここには何か違いがあるのだろうか．また調べてみたい．

##参考文献
Pythonで始めるアルゴリズム入門　伝統的なアルゴリズムで学ぶ定石と計算量
　　　　　　　　　　　　　　　　　　　　　　　　 増井 敏克　著　　翔泳社
AWS 認定 セキュリティ – 専門知識 合格までの道のり



# はじめに

皆さん、お久しぶり？です！！
ハンズラボのサムです！！
昨年の12月に記事を投稿してから1ヶ月ちょい間が空いてしまいした...
今回はタイトルの通りなのですが、先月の1/29にAWS認定 セキュリティ専門知識を受験し合格したので、
どうやって合格したのかなど体験談を書こうと思います！
中々AWS認定 セキュリティ専門知識の記事や参考書など少ないので少しでも皆様のお役に立てれば幸いです。

# 目的

今回社内のAWSセキュリティ関連のタスクをやることになりまして、その一環としてAWSのセキュリティ周りをちゃんと理解してないと感じたので、
認定試験を受けつつタスクをこなしていければ良いなと思い、受験することにしました！！
※という建前で、本当は昨年末に受けたAWS SAP認定が落ちたのが悔しくて...という動機があったのは内緒！

# スケジュール＆試験対策

本格的にセキュリティ周りのタスクを始めたのが1月の2週目位でその頃から学習をスタートし受験日が1/29なので約2週間程度のスケジュールでした。

1週目：ひたすら無料のAWS公式デジタルトレーニングを受講しました！特に解説系に重点をおいてユースケースなど把握していきました。
2週目：AWS公式トレーニングの練習問題付きのものを受講し、練習問題は答えを含めてなんでそうなのか把握できるまで反復して解きました！

1・2週目共通して下記の参考書を読み続けました！(内容が良かったので書籍とkindleの二重課金しました笑)
・解説はお風呂などで読む(kindle版)
・練習問題は隙間時間など区切って何度も解き直しました(書籍版)
https://www.amazon.co.jp/dp/4839970947/ref=cm_sw_r_tw_dp_SHC2WW222ZJYX70KF5EV

# 実際のテストの雰囲気など

実際に受験の際は下記の通りでしたが、試験会場はきれいで案内も丁寧でした。
試験の内容そのものについてはAWS SAPよりは簡単だけどAWS SAAよりは難しく、普通に問題文が5行以上の長文があったり、引っ掛け問題に近いものもあったのと、AWS試験特有の日本語が怪しい所も少しあったのできちんと対策しないと厳しいと思います。公式模試も受けたのですが、模試より問題は難しかったです。
試験時間は180分と短いようで長いですが、私の場合は見直しして20分程度残ったので、ペース配分にさえ注意してれば時間切れにはならないと感じました。

実際に試験を受けた会場は下記です！
申し込み機関：ピアソンVUE
試験会場：新宿駅前テストセンター
![スクリーンショット 2021-02-01 9.48.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181414/91bb1fb2-f732-f2c3-2048-08fd04a0743e.png)

# 結果

### まず当日に家で公式の模試を受けてみたのですが、結果が下記です。
合格ラインが75%なのでまさかの合格ラインに届いてないという恐ろしい結果に・・・。
![スクリーンショット 2021-01-29 10.31.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181414/86b42a9a-c86f-9303-6b07-7b30f9ef5f26.png)

### そして、本試験の結果がこちら
点数的に若干余裕があるとはいえ厳しい戦いでした。
模試のときよりも問題数が多いのと、きちんと見直しをしたので合格できたのかな？といった感じでした。
とはいえ、一部の領域は再学習の必要ありだったのでまだまだ勉強は必要ですね・・・
![スクリーンショット 2021-02-01 9.56.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181414/06849919-2fc5-366a-647a-749411a051e1.png)
![スクリーンショット 2021-02-01 9.56.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181414/83cedf52-5f9d-2e90-80a8-ffd4bcfb249d.png)

# 最後に

今回はAWSセキュリティ関連のタスクをこなすことが目的であって資格試験を取ることが目的ではなかったのですが、無事に取得できて本当に良かったです！
試験を通じてAWSセキュリティのユースケースやサービスなど色々知れたので、間違いなく為にはなりました！！
この結果を通じて会社やチームに貢献できるようにこれからも精進していきます！！
最後まで読んでいただき有難うございました！！

実際の試験の雰囲気など質問したいことがあれば分かる範囲でお答えしますのでTwitterにてどうぞ！
https://twitter.com/sam_eng3336
Android TWRP 起動時にinputコマンドを実行するとnot foundになってしまう

# 目的

- TWRP起動時にPCとAndroid端末をケーブルで接続後`adb shell input XXX`のコマンドを実行した時にnot foundと返されてしまう問題を一部解決したのでメモ的にまとめる。

# 方法

1. TWPRの「Mount」をタップする。

    ![20210116125207.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/5226afe8-a8c0-7dae-7678-88a08be8e04e.png)

1. 「System」にチェックを入れる。

    ![20210116125428.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/08b4f979-902f-ee34-843d-6bc7d347b773.png)

1. 任意のadbコマンドをPCとAndroid端末を接続して実行してみる。
1. not foundとなる問題は回避できた。（しかし筆者が実行したかったタップコマンドを実行すると「killed」とコマンドプロンプトに出力されて実行されていないようだった。）
slack or discordのdeploy botをサーバーレスで作る

## よくある悩み
- slackからデプロイコマンドを叩きたい...
- minecraftのサーバーコマンドをdiscord経由で実行したい...
- でもbot用のサーバー建てるのはちょっと...
- SaaS(IFTTT, zapier)探してもssh実行してくれる方法がなど無い(iOSにはssh経由でコマンドを実行するショートカットがあるのに...)

zapierでは、JS経由で頑張れば(npmをサポートしてないからwebpackをランタイムが不明だけど頑張る)sshはできないことはないですが、綺麗にsshする方法を発見しました!!!
今回はdiscordの例を示しますが、zapierは2,000以上のアプリケーションと連携でき、slackでも同様に使えます!!

## 全体構造
1. discordに発言する
2. zapierでdiscordをTriggerにして、discordに書かれた内容をgithubのコメントに書き込む
3. commnetをトリガーとしてgithub actionsを実行する

Java, Ruby, Goのみでしたら、[AWS Lambda](https://zapier.com/apps/aws-lambda/integrations)も有効です。

## 下準備
1. discordに専用チャンネルを作る
2. github issueを作成する

## zapを作成
discordの`New Message Posted to Channel`をTriggerとして、githubの`Create Comment in Github`を選択し連携します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165443/78e96581-adcb-33f6-4efb-ab917a6baaea.png)

実行されるコマンドはgitで管理するのに、discord→github連携の定義はコードに残せないのは気持ち悪いと思うかもしれませんが、ノーコード, サーバーレスに仕方がないは付き物です。

## .github/workflows/deploy.yml を作成
discordの誰の書き込みでもgithubにコメントされてしまいますのでdiscord上の設定で発言を絞るべきです。
Pull requestでも実行されるので注意が必要です。[公式sample](https://docs.github.com/ja/actions/reference/events-that-trigger-workflows#issue_comment)では
`if: ${{ github.event.issue.pull_request }}`と記述してissueのコメントだけ実行するようにしていますが、下記の例では、issue番号を指定し、特定のissueのコメントだけ実行されるようにしています。

使用可能なpayload object(github.evnetから参照できるもの)は[こちら](https://docs.github.com/ja/developers/webhooks-and-events/webhook-events-and-payloads#issue_comment)


```yml

name: deploy

# コメントで実行されたときにこのactionが実行される(Pull requestでも実行されるので注意)
on:
  issue_comment:
    types:
      - created

jobs:
  deploy:
    if: ${{ github.event.issue.number == 1 && github.event.comment.user.site_admin == true }} # 作成したissueの番号を指定
    runs-on: ubuntu-latest

    steps:
      - name: Deploy
        # ここにコマンドを書く
        run: echo "${{ github.event.comment.body }}"

      # コマンド実行が完了したことをdiscordに通知
      - name: Discord notification
        env:
          DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
        uses: Ilshidur/action-discord@master
        with:
          args: "${{ github.event.comment.body }} command done"
```

## その他注意点など
defaultブランチしかissue_commentは実行されません。
defaultブランチにpushした直後だとissueにcommentしても実行されないときがありました。次の日になったら反映されてました。

## 参考
https://docs.github.com/ja/actions/reference/events-that-trigger-workflows#issue_comment
dtostrf関数の使い方

#初めに
最近dtostrf関数を使用したが、使い方がよくわかっていなかったため調べた結果を忘れないうちに記録しておく。

#目次
[1. dtostf関数とは？](#1-dtostf関数とは)
[2. まとめ](#2-まとめ)

#1. dtostf関数とは？
dtosft関数はArduinoでよく使われる関数である。
機能で言うと、この関数は**小数点を含む数値を文字列に変換することが出来る関数**らしい。例えば、

```C:sample
double n = 123.456;
char m[50];

dtostrf(n, 5, 1, m);
```
の時、シリアルプリント等をしてmの値を見てみると、mの値は、nを**文字列**にして**5桁の文字列**、さらに**小数点以下は1桁**という値が入る。ここでいうと、**m = 123.4**となる。

では、文字列の長さの指定を長くしたらどうなるのか。

```C:sample2
double n = 123.456;
char m[50];

dtostrf(n, 10, 1, m);
```

このように第2引数を10にすると、**m ＝ ␣␣␣␣␣123.4** のように右詰めで空白ありの10桁の文字列になる。

また、第1引数が指定した文字列の長さが整数部分より短かったら整数部分が全部出るように長さが追加されるぽい。

#2. まとめ

まとめるとこう。書いてないけど、mは文字列バッファです。

![688EB9B8-F9FE-403D-9E88-FA3B533C6FB0.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/935940/382e3dd3-97c6-9298-1d69-92305d79a47c.jpeg)


、、、dtostrf
これって英語で言うと double to string format なのかな
メールセキュリティとDNSの蜜月関係

# メールセキュリティとDNSの蜜月関係


2021.1.27-29 開催された JANOG47 で、弊社チーフエンジニア平野善隆(@hirachan)により「メールセキュリティとDNSの蜜月関係」というタイトルで発表が行われました。その時の資料をこちらにも共有します。


## はじめに

メールの配送先を指定するMXレコードに始まり、送信ドメイン認証などのメールセキュリティに関連する様々な設定もまたDNSを利用しています。

そういった中、メールの暗号化を担保する仕組みとしてDNSSECを前提としたDANEというプロトコルがヨーロッパを中心に普及しはじめています。今まではDNSのレコードを時々書くだけでメールセキュリティの設定ができていたのに、DNSSECに対応できていないとメールセキュリティの設定ができません。また、似たような仕組みであるMTA-STSはポリシーをwebサーバーで公開します。つまり、webサーバーを管理できないとメールセキュリティの設定ができません。

このような、新たなメールセキュリティの仕組みの紹介と、円滑な運用方法について議論できればと思います


## 目次

- メールセキュリティの全体像
- 世界のメールセキュリティ事情と日本の状況
- メールとDNS
  - 送信ドメイン認証
  - メールの暗号化
- まとめ


## メールセキュリティとDNSの蜜月関係
[![thumbnail](https://files.speakerdeck.com/presentations/e206b61040dc45b786b7391ea07a3d03/slide_0.jpg?17232536)](https://speakerdeck.com/hirachan/merusekiyuriteitodnsfalsemi-yue-guan-xi)


## 関連発表資料

昨年JPAAWG 3rd General Meeting で発表した資料も合わせてご参考ください。

- [送信ドメイン認証・暗号化 DeepDive ～ DMARC から MTA-STS, DANE まで全部PASSさせるまでの道のり](https://qiita.com/qualitia_cdev/items/9f01f4151ceafca1ef8d)
Setting DB SQL Mode sql_mode

Here I am checking the value of sql_mode ,changing it's default value and playing with mysql data types. I am using sequel pro for this exercise.
This exercise is about INT and VACHAR data type mainly but you can refer for any other as well. Many times we write string in our query in place of INT(vice versa), and the query get executed successfully. Let's see why such queries get no error and check their impact on our data.
 
## sql_mode
execute this query in sequel pro
```-- QUERY1
SHOW VARIABLES LIKE 'sql_mode';
```
Result:↓

 Variable_name|	Value
--------- | ---------
sql_mode|	

```
UPDATE
    `table_order`
SET
    `id` = '92b', //id is int 😀
    `date` = NOW(),
    `customer_id` = '786'
WHERE
    `order_number` = 7;
```

id(INT) | order_number(INT) | customer_id(INT)
--------- | ---------| ---------
92| 	7	| 9999

- Above query executed without any error。id -> INT(4)。'92b' become　92 。It can be a data entry error ,92b in place of 9 or something like that.
- Let's set the DB sql_mode to Strict、So only the INT value can enter in INT field and same for the other data types(varchar、text,,,)

```-- Strict mode SETTING
SET sql_mode = 'STRICT_ALL_TABLES';  
SHOW VARIABLES LIKE 'sql_mode';　
```

 Variable_name|	Value
--------- | ---------
sql_mode|	STRICT_ALL_TABLES

- let's execute the same query again, -> QUERY1
- can't execute how it's funny.we got some error now ->「Data truncated for column 'id' at row 1」
- id　column is INT(4)。So it cant take string value as input. 🎉🎉🎉


But the following can be executed in strict mode, let's see why?
```UPDATE `table_order` SET   `id` = '12' WHERE  `order_number` = 9;
```
In this case the mysql will do auto type casting here, conver '12' -> 12 then store in id field.But its not recommend to put unnecessary workload on mysql side.Always correct the data types while writing queries.

### reference
- https://dev.mysql.com/doc/refman/5.6/en/sql-mode.html
- https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html
- https://stackoverflow.com/questions/21762075/mysql-automatically-cast-convert-a-string-to-a-number


## sql_safe_updates for beginner only(others can skip this)
Beginner in mysql many times forgot to write where clause with UPDATE or DELETE query, it can be helpful for beginners only(I also forgot many times when I stared writing sql queries) 
- SHOW VARIABLES LIKE 'sql_safe_updates'
- SET sql_safe_updates=1;　// OFF：0 、 ON：1

Variable_name	| Value
--------- | ---------
sql_safe_updates	| ON
```Query without where clause
update `table_order` set `customer_id` = 77
```
- While executing the above query, we got the following error↓
「**You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column**」

### reference
https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html
5分でできる。nuxt/vueで自動画像圧縮の方法（超絶簡単）

# 目次
・[画像圧縮について](#画像圧縮について)
・[画像圧縮は自動でやってしまおう](#画像圧縮は自動でやってしまおう)
・[next-optimized-imagesを入れてみる](#next-optimized-imagesを入れてみる)


## 画像圧縮について
画像圧縮って手動でするの面倒ですよねー。
それが、png・jpeg・svgなどそれぞれで圧縮するとなるとさらに面倒ですよねー。
サイトパフォーマンス的にも画像は軽くしておきたいけど、自動でどうかならんかなーって思いますよね。

## 画像圧縮は自動でやってしまおう
ということで手動で圧縮するのは面倒なので自動で圧縮してもらいましょう！

まず必要なのは
[next-optimized-images](https://github.com/juliomrqz/nuxt-optimized-images)
が必要です。
あとは、圧縮したい画像形式によって必要な
[Optimization Packages](https://github.com/juliomrqz/nuxt-optimized-images#optimization-packages)
を入れますー。

## next-optimized-imagesを入れてみる
では、実際にnext-optimized-imagesを入れてみましょう。

```
yarn add --dev @aceforth/nuxt-optimized-images imagemin-mozjpeg imagemin-pngquant imagemin-svgo
```
これを叩くだけで入りますー。

あとはnext.config.jsに下記を記述しますー。

```next.config.ts
{
  buildModules: [
    '@aceforth/nuxt-optimized-images',
  ],
  optimizedImages: {
    optimizeImages: true
  }
}
```
これで終わりですーw

ただ、next-optimized-imagesは
>optimized for production builds, not development builds.

とあるので、開発では実行されず、本番用で実行されるのがデフォルト設定となってます。
ですので、開発でも実行させたい場合は

```next.config.ts
{
  buildModules: [
    '@aceforth/nuxt-optimized-images',
  ],
  optimizedImages: {
    optimizeImages: true,
    optimizeImagesInDev: true,
  }
}
```

```
optimizeImagesInDev: true,
```

を追加するだけで開発でも実行されます。

これで、もう手動での画像圧縮とはおさらばですねー！
素敵な画像圧縮ライフをお送りくださいー。:laughing:
【Git 初心者向け】 バイナリファイルがコンフリクトした場合の対処法

## Git初心者あるある

誰もが最初に苦戦するコンフリクトの話です。  
テキストファイルは無理矢理直すことができると思いますが、バイナリファイルがコンフリクトすると一気解消できなくなる人が急増する印象です。

今回はそのバイナリファイルのコンフリクト解消を比較的工数少なく行うことができる方法を紹介します。

## 例: masterブランチと開発ブランチがコンフリクトしてプルリクエストでマージできない

### 1. 開発ブランチにmasterブランチを取り込む

```bash
$ git pull --rebase origin master
```

### 2. どのファイルがコンフリクトしているか確かめる

```bash
$ git status
```

### 3. コンフリクトしていたファイルをmasterブランチ側に寄せる

```bash
$ git restore --ours <コンフリクトしているバイナリファイル>
```

- oursオプション、theirsオプションについてはこちらを参考にしてください。
    - https://qiita.com/nirasan/items/27cdd75e8117bf41d2be

### 4. コンフリクトしていたファイルを開き、自身が追加したい差分を追加・編集する
- ファイルを開いたら、masterのファイルと同じ状態のものが表示されるはずです。

### 5. コンフリクトしていたファイルをステージングに上げる

```bash
$ git add <コンフリクトしていたバイナリファイル>
```

### 6. ステータスを確認し、全てのファイルがステージングに上がっているか確認する

```bash
$ git status
```

### 7. リベースを続ける(コンフリクト解消完了)

```bash
$ git rebase --continue
```

### 8. プッシュする（強制）

```bash
$ git push --force-with-lease origin <ブランチ名>
```

## 注意
- 開発ブランチに複数の開発コミットがありrebaseでコンフリクトした場合、複数回コンフリクト解消しなければいけないときがあります。事前に開発コミットをまとめてからコンフリクト解消することをおすすめします。
    - コミットをまとめる方法はこちら
        - https://qiita.com/KTakata/items/d33185fc0457c08654a5

- バイナリファイルのコンフリクトは一旦どちらかに寄せなければいけないので、修正した差分を忘れてしまいそうな場合はGit管理外のフォルダーにバックアップを取っておくことをおすすめします。

## まとめ

- Gitの操作で時間を無駄にしてしまうのは勿体無いので、効率の良い方法を覚えて素早く開発していきましょう！
【初心者向け】"良い記事"の書き方


この記事では"良い記事"を書くための**基本事項**をまとめました。<font color="red">**記事を書き始めたばかりの初心者**</font>の助けとなることを期待しています。

# [ 1 ] "良い記事"とは？

"良い記事"には次の四点が必要です。以降では、これらを達成するための基本事項を紹介します。

1. **正しい内容**
2. **役立つ内容**
3. **読みやすい構成**
4. **読みやすい表現**


# [ 2 ] 正しい内容

正しい内容の記事を書くために、以下の点を押さえましょう。

## ( 1 ) リファレンスを示す

**使用したリファレンスを必ず示しましょう**。これにより、[剽窃](https://ja.wikipedia.org/wiki/Wikipedia:%E5%89%BD%E7%AA%83)による著作権の侵害やトラブルを避けことができます。また、リファレンスとしては信用度の高いものを選ぶことをおすすめします。

## ( 2 ) 間違った説明を避ける

**明確に間違った説明を避けましょう**。記事の信用を低下させないために、十分な調査をすることをおすすめします。

## ( 3 ) 誇張した表現を避ける

**記述を誇張して表現するのは避けましょう**。間違いを生む原因になるので、まずは内容をありのままで表現することをおすすめします。

# [ 3 ] 役立つ内容

役立つ内容の記事を書くために、以下の点を押さえましょう。

## ( 1 ) 1年後の自分を想像する

**1年後の自分が理解できる内容にしましょう**。メモ程度の内容でもそのメモを理解するための情報を加えるだけで1年後の自分の読みやすさが格段に上がります。

## ( 2 ) 他人を意識する

**他人に役立つ内容にしましょう**。他人に役立つか判断できない場合は友人などに意見をもらうと良いと思います。

# [ 4 ] 読みやすい構成

読みやすい構成の記事にするために、以下の点を押さえましょう。

## ( 1 ) トピックセンテンスを書く

**トピックセンテンスを書いてから詳細を説明しましょう**。トピックセンテンスとは概要を示す文のことです。

また、ほとんどの記事では以下のような構造になるので、それぞれの初めにトピックセンテンスを設けるようにしましょう。

```
記事 - 章 - 節
```

## ( 2 ) 記事の目標を決める

**書き始める前に記事の目標を決めましょう**。目標をあらかじめ決めることで筋の通った記事を書くことができます。

## ( 3 ) 実行環境を示す

**実行環境を記事の初めに示しましょう**。OSやOSのバージョンが異なる場合は記事と同じ動作をするとは限りません。

# [ 5 ] 読みやすい表現

読みやすい表現にするために、以下の点を押さえましょう。

## ( 1 ) Markdownを使う

**Markdownの記法を使いましょう**。これにより、記事の表現力が格段に上がります。また、[Markdown記法 チートシート](https://qiita.com/Qiita/items/c686397e4a0f4f11683d)にあることを把握すれば十分です。

## ( 2 ) 余白を使う

**空行や箇条書きを使って余白を入れましょう**。ただし、余白が多すぎても少なすぎても読みにくさの原因になるので、読みやすい余白の量に調節しましょう。

## ( 3 ) 文体を統一する

**記事全体で文体を統一しましょう**。特に、敬体（ですます調）と常体（だである調）を混ぜている記事は違和感を覚えます。

#[ 6 ] 最後に

"良い記事"を書くために特に必要な基本事項をこの記事ではまとめました。

次週は、「【初級者向け】"良い記事"の書き方」という続編を書く予定です。
【aws-cli】S3のオブジェクト一覧から最終更新日時の範囲検索をして件数を取得するワンライナー

S3上のファイル情報をローカルで見たくて色々したときのメモです。

#### S3上での最終更新日時を日付の範囲で絞ったものを表示したいとき

2020-11-03 〜 2020-11-15 の範囲の場合

```
aws s3 ls s3://bucket_name/directory_name/ | awk '$1' | grep -E '2020-11-(0[3-9]|1[0-5])'
```

#### S3上での最終更新日時が特定の年月のものを表示したいとき

```
aws s3 ls s3://bucket_name/directory_name/ | awk '$1' | grep -E '2020-11-'
```

#### S3上での最終更新日時が特定の年月で、ファイル名の一覧だけを表示したいとき

```
aws s3 ls s3://bucket_name/directory_name/ | awk '$1' | grep -c -E '2020-11-' | awk {'print $4'}
```

#### S3上での最終更新日時が特定の年月で、ヒットしたファイル数を表示したいとき

```
aws s3 ls s3://bucket_name/directory_name/ | awk '$1' | grep -c -E '2020-11-'
```

#### S3上での最終更新日時が特定の年月で、ヒットしたファイルの総バイト数を表示したいとき

```
aws s3 ls s3://bucket_name/directory_name/ | awk '$1' | grep -c -E '2020-11-' | awk '{s += $3} AND {print s}'
```
GitHub PagesのminimaテンプレートでGAのデータが送信されない

趣味サイトで、GitHub Pagesのminimaテンプレートをカスタマイズして使っているのですが、
Google Analyticsのデータが送信されずに困っていました。

## 原因

2021/02/01 時点でのGitHub Pagesでは、下記を見る限り、minima	2.5.1が動いているはずだが、
https://pages.github.com/versions/

挙動から推測する限り、2.5.0が動いている。
そのため、下記対応が取込されておらず、Google Analyticsのデータが送信されない。
https://github.com/jekyll/minima/pull/281

## 対策

下記ファイルを[minimaリポジトリ](https://github.com/jekyll/minima)の2.5.1のもので上書きする。
docs/_includes/google-analytics.html

## 参考

https://github.com/jekyll/minima/issues/561
composer self-update で Command "self-update" is not defined. と表示される場合

composer self-update で Command "self-update" is not defined. と表示される場合の対処の仕方です。

まずは apt 系で入っている場合は削除します。

````
sudo apt-get remove composer;
````

次に、公式サイトからのインストールスクリプトのダウンロードを行います。

````
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');";
````

/usr/binにcomposerをインストールを行います。

````
sudo php composer-setup.php --install-dir=/usr/bin --filename=composer;
````

これで composer self-update が使用できるようになります。

````
composer self-update;
````

※追記
インストール先を/usr/local/binと記述していました。正しくは/usr/binになります。
お知らせありがとうございますm(_ _)m
【SNS認証】本番環境でSNS認証を実装する前に行うこと

# この記事を描くきっかけになった経緯
ローカル環境で開発が終わり、本番環境（Herokuを用いる）にデプロイした際に
SNS認証に関する２つのエラーが発生しました。
それぞれ備忘録として残しておきます。

# エラー原因１：本番環境（Heroku上）に環境変数を定義していなかった
本番環境（Heroku上）で環境変数を定義するには、ターミナルで下記を実行する。

```:ターミナル
% heroku config:set 環境変数名='値'
```
# エラー原因２：外部APIの設定にて承認済みのリダイレクト URIが設定されていなかった
Google Cloud Platformの設定画面にて、ローカルの時に設定したように
承認済みのリダイレクト URL　にパスを追記する必要がある。
![スクリーンショット 2021-02-01 7.46.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1016288/afb0d6f0-bfaf-45df-ff10-a47dbfeb8e10.png)



# 終わりに
これを機に環境変数はローカル環境、本番環境の両方に必要だということが身につきました。
またエラーに対しても落ち着いて対応すれば自力で解決できるという経験を積み、前向きに対応できるようになってきたと感じます。
オブジェクト、インスタンス、クラスについて

#オブジェクト
文字列、数値、変数、配列のこと
メソッドによって文字列、数値、変数、配列を操作することができる

オブジェクトは何らかのクラスに所属している
``3``や``8``は``Integerクラス``
``hello``は``Stringクラス``に所属している

オブジェクトはクラスから作ることができる

#インスタンス
クラスという設計図を元に作られたオブジェクト

オブジェクトがあるクラスに所属していることを強調したい時にインスタンスと言う

インスタンス　≒　オブジェクト

#クラス
データと処理をまとめたもの

データ　＝　インスタンス変数（クラス内で定義された変数）
 処理   ＝　インスタンスメソッド(クラスの中に書いたメソッドのこと)

インスタンス変数はクラスの中でしか使えない

```ruby
class Student #クラス名
  def initialize(name)
    @name = name #インスタンス変数を定義
  end

  def avg(a,b) #インスタンスメソッド
    p @name,(a + b) / 2
  end
end

a = Student.new("sato") #オブジェクト化、オブジェクト生成、インスタンス化

a.avg(80,70) #メソッドの実行
```
オブジェクト生成
→``クラス名.new``
→そのクラスのインスタンスメソッドが使えるようになる
ASP.NET Coreに渡される環境変数を、起動スクリプトで成型する

# はじめに
.NET の構成情報は、Program.csの定義の仕方によって、設定ファイルや環境変数、コマンドライン引数、その他設定値ストアの値を優先順序を付けて読み込むことで設定値を柔軟に定義することができます。

- [ASP.NET Coreの構成](https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/configuration/)
- [.net coreにおける設定値の解決](https://karuakun.wordpress.com/2018/11/16/1345/)

アプリとしては上記の設定値の解決でどうにかなるはずです。ただ、例えば.NETの設定情報が下記のようになっているんだけれど、環境変数は他のシステムで使いまわしていて、`QIITA_API_BASEURI`という名前で変数の中には`HTTPS://qiita.com`といった文字列が格納されている場合は少し困ってしまいます。

``` json:appsettings.json
{
    "Qiita": {
        "ApiBaseUri": "https://qiita.com/api"
    }
}
```

この記事では、アプリケーションの起動時にシェルスクリプトを起動して環境変数を成型する方法について説明します。

# Dockerfileの修正

Visual StudioでDockerfileを追加した場合、次のようなDockerfileがプロジェクトに追加されます。

``` Dockerfile:Dockerfile
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
```

dotnetコマンドを直に起動するのではなく、シェルスクリプト経由で起動するようにfinalステージを書き換えます。

``` Dockerfile:Dockerfile（finalステージのみ抜粋）
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
COPY entrypoint.sh .
RUN chmod 755 entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]
```

# 起動スクリプト

起動スクリプトでは、環境変数を編集して再設定します。
開発時は`QIITA_API_BASEURI`が設定されてこないこともあり、その場合は設定ファイルを優先したいので場合分けをしています。

``` entrypoint.sh
#!/bin/sh

if [ -n "$QIITA_API_BASEURI" ]; then
    export Qiita__ApiBaseUri="http://${QIITA_API_BASEURI}/api"
fi
dotnet WebApplication1.dll $@
```

# まとめ

設定情報のフォーマットを動的に変更する必要が出てきた場合、起動スクリプトを使うと柔軟に修正できます！
GAS Date 2月だけに起こるバグ

# 条件
[GAS](https://developers.google.com/apps-script) で　下記のコード

```JavaScript
target = new Date('4:00:00 AM');
```
とすると

```
Sat Dec 30 1899 10:00:00 GMT+0900 (Japan Standard Time) {}
```
と解釈される
2月1日に

```JavaScript
const now = new Date();
target.setFullYear(now.getFullYear());
target.setMonth(now.getMonth());
target.setDate(now.getDate());
```
すると

```JavaScript
target.setFullYear(2021);
target.setMonth(1); // 0から始まるので 1は2月
target.setDate(1);
```
と実行される

# 期待値
これを実行すると

```
Mon Feb 01 2021 10:00:00 GMT+0900 (Japan Standard Time)
```
となって欲しい

# 発生した事象
しかし実際にこれを実行すると

```
Mon Mar 01 2021 10:00:00 GMT+0900 (Japan Standard Time)
```

あれ？

# 原因
```JavaScript
target.setFullYear(2021);
// Thu Dec 30 2021 10:00:00 GMT+0900 (Japan Standard Time) 単純に年が2021となる
target.setMonth(1);
//Tue Mar 02 2021 10:00:00 GMT+0900 (Japan Standard Time)  2月30日つまり 3月2日と解釈される
target.setDate(1)
//Mon Mar 01 2021 10:00:00 GMT+0900 (Japan Standard Time) 3月2日　の"日"の部分が 1になる
```
なるほどなるほど　2月だけ30日がないから
今まで問題なかったけど　本来は　日付を30->1にしないと　正しく動かないのか

# 解決策
```
const now = new Date();
target.setFullYear(now.getFullYear());
target.setMonth(now.getMonth(),1); //日付を30->1にする
target.setDate(now.getDate());
```
Route53の「フェールオーバールーティング」と「DNSフェールオーバー」の違いを調べてみた


# はじめに
* AWS ソリューションアーキテクトアソシエイト試験に向けて勉強中
* Route53の「フェールオーバールーティング」と「DNSフェールオーバー」の違いがよくわからなかったので、調べてみた

# Route53の「フェールオーバールーティング」とは
* シンプルルーティング、加重ルーティング、複数値ルーティングなどのルーティングポリシーの一つ
* アクティブ/スダンバイ方式
* アクティブ側のシステムへのヘルスチェックが失敗した場合、スダンバイ側システムへルーティングする
* Sorryサーバーを登録しておけば、本番機の障害時にSorryコンテンツを表示させることができる

# Route53の「DNSフェールオーバー」とは
* 上記ルーティングポリシーとは関係なく、フェールオーバーを実現できる機能
* 上記ルーティングポリシー＋本機能を組み合わせることが可能
* 例えば、正常時はシンプルルーティングなどで全てのインスタンスにトラフィックを分散させて、障害時は本機能により、障害が発生しているインスタンスにはトラフィックを送らない、という設定が可能

# 参考
* https://www.kkinternational.work/2020/08/19/learn-aws-7/
Pythonでデコレートする関数の引数情報を伝搬させる

# 対象読者
- pythonでデコレータを作成したことがある方
- pythonで型注釈を活用したコードを書いている方
- python3.5以上（多分3.5以上で有効なコードです）

# はじめに
pythonのデコレータはよく利用すると思いますが、デコレートしてしまうと、元関数の引数情報が失われてしまったり、一体何が返っているのか分からないというデメリットがあります。

最近では型注釈が発達してきて、この問題を解決できるようになってきているので、実装例を紹介します。

確認はVSCode(Pylance + mypy)でやりましたが、IDEや環境次第では型情報の認識の仕方が異なるかもしれません。

# 実装例

まず、基本形から。
次のように、型変数を定義して、それを戻り値の型にしてあげれば関数の情報を伝搬できます。

``` python
from typing import Callable, TypeVar
F = TypeVar("F", bound=Callable)

def decorator(func: F) -> F:
  ...
  return func

@decorator
def func(num1, num2):
  return num1 + num2
```

次に、クラスでデコレータを作成する方法を紹介します。
ただし、この方法は一般的な方法じゃないので、こういうことができるよって知識レベルに留めておきましょう。

ご利用は、自己責任でお願いします。

``` python
from functools import update_wrapper
from typing import Any, Callable, Generic, TypeVar

F = TypeVar("F", bound=Callable)


class FuncWrapper(Generic[F]):
    def __init__(self, func: F):
        update_wrapper(self, func)  # 元の関数を__wrapped__に保持する。など、ラッパー作成のヘルパー関数
        # update_wrapperだけでは不十分な情報を保持する
        self.__code__ = func.__code__
        self.__defaults__ = func.__defaults__
        self.__kwdefaults__ = func.__kwdefaults__

    def __getattr__(self, name):
        return getattr(self.__wrapped__, name)  # 未定義の属性は元の関数を参照しにいく

    @property
    def __call__(self) -> F:
        return self.__wrapped__

    @property
    def __class__(self):
        return self.__wrapped__.__class__  # とりあえず、返すクラスも擬態する

@FuncWrapper
def func(num1, num2):
  return num1 + num2
```
Genericで型変数を保持し、それをどこかで利用します。

キモは、`__call__`をメソッドじゃなくて、プロパティにしてしまったことですね。
関数の引数と戻り値情報を取り出すのは至難の業ですし、取り出したとしても__docstring__が失われてしまうので、これが一番簡単だと思います。

なお、FuncWrapperをinspectモジュールで一通り分析しており、確認した範囲では元関数と同じ判定結果となっています。
もちろん、擬態しているだけなので、何かしら副作用が生じる可能性はあります。

# pep612 Parameter Specification Variables
デコレータの型注釈でみんな悪戦苦闘しているようで、引数情報をもっと簡単に扱うための機能導入が検討されています。

- [pep612](https://www.python.org/dev/peps/pep-0612/)

# さいごに
結果だけ見れば簡単なことでしたが、`__call__`をプロパティにするという発想が中々できず、結構時間を費やしました。。。
録画サーバー構築(Ubuntu20.04.1 LTS+Mirakurun+Chinachu)

まずはテスト録画ができるように
-
+ ３年以上前にPT3の中古テレビカードは入手済み

> 何もしていなかったがついにLinux上での稼働に取り組む。
> 一度32ビット上で構築しrecpt1で録画テストし成功、ドライバーからコンパイルした
> epgstation導入したところドライバーはOS付属でよいとのことで64bitの最新Ubuntu入れ直し
> recdvbで録画テストして再生すると画質が悪かった。後でソース見るとrecdvbは/dev/dvbのデバイスのみ検索（画質が悪い原因は何の原因かはわからないのでドライバーが原因と確定しているわけではない）
> epgstationまでインストールしたが、その時はなぜか視聴ボタンが表示されず
> epgstationの問題が何かわからず結局元の録画テストの画質がとても欲しくなり32bitでやった作業、つまりpt3_drv.koの作成ドライバーの作成とそれを認識させて標準のドライバがロードされない環境に
> できればdockerなど使用しない方法が好きだったがmirakurunが必要なのでその方式で

作業内容
-
* [pt3ドライバコンパイルと装着recpt1,sambaなど(参考サイト)](https://www.jifu-labo.net/2015/09/ubuntu_pre/#i-2)
* 上記の方の通りで構築できた。さらにsoftcasで行った(これはsoライブラリの入れ替えでOK）
* softcasはコンパイルの知識があればできるがここではリンクや詳細は記載しない
* recpt1-http-rev4/ はコンパイルリンクまで行ったがインストールはせず。インストールは必要ないと思われる
* recpt1 --b25 --strip 27 18 nhktest.ts で映像に問題ないことを確認
* mirakurunのインストール（上記リンクの手順で問題なし）。またmirakurunが正常に動いてさえいればepgstationもchinachuもmirakuranとの連携、つまりconfigのパス設定さえ正しく記載できていればOKということ

> mirakuranがデバイスを正しく認識しているか: ./config/tuners.ymlのデバイスのパスが正しいか
> 上記パスはrecpt1の場合はデバイスを /dev/pt3video0 からになっているか
> commandでrevdvbの場合又、/dev/dvb/adapter0/dvr0 などのパスの場合は標準OSでリンクされるデバイスドライバ構成
> chinachuで最も重要な設定部分は(おそらくepgstationでも同様)config.jsonのmirakurunPath
> Ubuntu 20.04.1で普通にmirakurunのインストールでは/var/run/mirakurun.sockが実行時に存在しているはず
> "mirakurunPath": "http+unix://%2Fvar%2Frun%2Fmirakurun.sock/", <最後はカンマ含める
> 上記のように環境に応じて書き換える(スラッシュ=/ はURLエンコード型に置き換える)


以上ハマったところなどを記載

追記：
* rivarun コマンドでの録画テストができずにいたがこのコマンドはmirakurunサービスを取得する。このコマンドでは--mirakurun指定は[host:port]のみで/var/run/mirakurun.sockのような記載はできないと思われる。またこのmirakurunのパラメータはデフォルトでは必要はなかった。
(フジテレビを10秒/20秒など録画コマンド例）
* rivarun --b25 --mirakurun localhost:40772 --ch GR/21 10 fujitest2.ts
* rivarun --b25 --ch GR/21 10 fujitest3.ts
* rivarun --b25 --sid 1056 --ch GR/21 20 fuji20.ts
(--sidサービスIDの指定は地上波であればなくてもよい)
初投稿　Date today .wdayを用いた記述

初めての投稿となります。
日々学習している内容をアウトプットできたらと思い投稿します
本日学習した内容を投稿します。
本日はDate.today.wdayを用いて
月曜日は「今日は月曜日だ」
水曜日だけ「今日は水曜日ノー残業デーだ！」となるように記述してください。

#回答がこちらです


```ruby
require "date"

day = Date.today.wday
days = ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"]

if day == 3
  puts "今日は#{days[day]}ノー残業デーだ！"
else
  puts "今日は#{days[day]}だ"
end
```

Dateクラスをライブラリから呼び出します。
（3行目）wdayメソッドを用いて曜日を0（日曜日）から6（土曜日）の整数にしたときの「今日」の値を取得します。
（4行目）配列daysを定義し、日曜日（0番）〜土曜日（6番目）まで文字列を格納します。
（6〜10行目）dayの値が３（水曜日）か否かで条件分岐させます。たとえば、今日が金曜日だった場合はday=5となり、daysの5番目の値である金曜日が出力されるようになります。

- マークダウン記法とやらも全くわかっていないのでここでアウトプットさせていただきます。
これからもよろしくお願いします。
ハープの弦をRで簡単在庫管理　

最近Rを始めて、その素晴らしさに感動して自分も初心者ながら何かお役に立てればと思い初投稿しました。
大変基礎的な内容で、もっと良いコードがあるかと思います。もしもっと良いコードが見つかったら適宜更新します。
コードが役に立つかというよりかはDFの変数名をコピペして使うとかの方かもしれません。

完成したcsvをハープ弾く方に渡せば喜ぶこと間違いなしです。　

ハープの弦の管理がすごく面倒な楽器です。
こちらはグランドハープ４７弦のバージョンです。
グランドハープは、高音域はナイロンかガット（奏者のこだわりによる）、中音域はガット、低音域はワイヤー弦を張るようにつくられており、特に夏場に中高音域の弦がよく切れるため、気が付かないうちに在庫がなくなっていたりします。また、発注するときにどの弦か確認するのが結構面倒です。弦は①オクターブのどの位置にあるか②弦独自の番号で整理されてます。が、お店によっては両方の表記がなかったりして、とにかく面倒。ネットで調べてもあのハープのネックに弦の一覧が書いてあって、結構見づらいので、

そこで、今回はRで
〇全音域
〇高音域のナイロン
〇中高音域のガット
〇低音域のワイヤー

に分けてデータフレームにて在庫管理をするようにしてみました。本コードはグランドハープに特化していますが、今後３４弦前後のレバーハープなどにも対応できたらと思います。　

以下のコードに在庫を入力していけば管理できます。　

まずは、全ての弦の在庫管理のデータフレームの作成です。なぜ00があるかというと、一番上のソ(G)は00の表記で、その次が0にファ(F)なるからです。

```r
#all strings 〇全部の弦
Number_all  <- c("00",0:45)
octave_all <- c("above 1st G","above 1st F","1st E","1st D","1st C","1st B","1st A", "1st G", "1st F","2nd E","2nd D","2nd C","2nd B","2nd A", "2nd G", "2nd F",
            "3rd E","3rd D","3rd C","3rd B","3rd A", "3rd G", "3rd F","4th E","4th D","4th C","4th B","4th A", "4th G", "4th F","5th E","5th D","5th C","5th B","5th A", "5th G", "5th F",
            "6th E","6th D","6th C","6th B","6th A", "6th G", "6th F","7th E","7th D","7th C")
stock_all<- c(rep(0, length = 47)) 
( all    <- data.frame(Number=Number_all, Octave=octave_all, stock=stock_all) )
```


続いてナイロンの弦ですが、こちらは人によって張る範囲が変わります。一番可能性として広い範囲で入れてみました。

```r
#nylon strings 〇ナイロン弦
Number_nylon  <- c("00",0:14)
octave_nylon <- c("above 1st G","above 1st F","1st E","1st D","1st C","1st B","1st A", "1st G", "1st F","2nd E","2nd D","2nd C","2nd B","2nd A", "2nd G", "2nd F")
stock_nylon<- c(rep(0, length = 16)) 
( nylon    <- data.frame(Number=Number_nylon, Octave=octave_nylon, stock=stock_nylon) )
```

一番頭を悩ませる、ガット弦です。太いのはそこまで切れない、とは思いますが、高音域は夏は本当によく切れます。一番在庫管理を気にしないといけない音域です。私の場合だとNo.8、2オクターブのミ(E)が在庫切れになります。　

```r
#gut strings 〇ガット弦
Number_gut  <- c(8:33)
octave_gut <- c("2nd E","2nd D","2nd C","2nd B","2nd A", "2nd G", "2nd F",
                "3rd E","3rd D","3rd C","3rd B","3rd A", "3rd G", "3rd F","4th E","4th D","4th C","4th B","4th A", "4th G", "4th F","5th E","5th D","5th C","5th B","5th A")
stock_gut<- c(rep(0, length = 26)) 
( gut   <- data.frame(Number=Number_gut, Octave=octave_gut, stock=stock_gut) )
```

最後に、みんな大好きワイヤー弦！です。こちらは、定期メンテナンスで自分で張り替える時とかもあると思うのでご活用ください。たまに切れると落雷のような音がしますが、、おそらく数年に一度あるかないかのイベントでしょう。　

```r
#wire strings 〇ワイヤー弦
Number_wire <- c(36:47)
octave_wire <- c("5th G", "5th F","6th E","6th D","6th C","6th B","6th A", "6th G", "6th F",
"7th E","7th D","7th C")
stock_wire <- c(rep(0, length = 12)) 
( wire  <- data.frame(Number=Number_wire, Octave=octave_wire, stock=stock_wire) )
```
ストックはポチポチとstock欄に入れて、最後csv出力で完了。readrパッケージで文字化けを防止してます。。。　ただ、そのあとエクセルで00が0に自動修正されることに要注意。

```r
#write csv csv出力　
readr::write_excel_csv(all, "all_strings.csv")　  全部の弦のcsv 
readr::write_excel_csv(nylon, "nylon_strings.csv") ナイロンのcsv
readr::write_excel_csv(gut, "gut_strings.csv")     ガットのcsv 
readr::write_excel_csv(wire, "wire_strings.csv")   ワイヤーのcsv
``` 


qiitaで需要があるのかちょっと謎ですが（笑）、Enjoy your harp life！:blush:
クライアントのデータ分析を始めて当たった壁

# 0.はじめに

データ分析とは言っても、ホントに今まで大した分析をしてこなかった会社で、
ようやく「分析、した方がよくない？流行りだし」レベルでちょっとグラフ化したり、
求められてるのはそのレベルだけど、まだ始めたばかりだけどぶつかっている壁を共有...

# 1. そもそも分析対象のデータがまともじゃない

というのも、残しているデータが使い物になるデータと、使い物にならないデータがあって、
弊社は契約からストックで課金していくタイプのシステムですが、

運用上、ホントは解約してないけど解約のコードが入力されていたり、
特殊なアカウントだったりが多すぎて、え？このクライアントって今どういう状態なの？というのが、
データやシステムで確認できず、俗人化が激しく、手作業で分類していかなければいけないケースがある。

# 2.分析に必要なデータが無い

こういうクライアントは長期契約につながりやすいよね。という結果が出したいので、
クライアントの属性が必要なのに、データとして残っているのは精々会社の住所と代表者名とか電話番号諸々とかくらい。

現実的には従業員数とか、設立年とかあったら、
「うちのシステムはこれくらいの従業員数だと需要あるよね」とか、「まだ若い会社にウケがいいよね」とか出せるのに、

今のデータでは「電話番号の末尾が4の会社には長く使ってもらえるよね」とか「代表者が小林の会社はすぐ解約されちゃうよね」みたいな
トンチンカンな分析結果しか出せない...

# 3.データベースの保存状況が悪い

メインのデータベースがオンプレなので、もちろん日次のバックアップは数年間に渡って保存しているが、過去のデータを確認するためには、
dumpデータをAWSのグレイシアから解凍して、S3に移動、S3からダウンロードして（30GB）、分析環境のデータベースにdumpを流すという途方もない作業が必要になる。

そもそもデータベースがRDSに載っていればそのまま分析ツールが使えるのに...とか思いつつ、
手動で頑張って分析を試みているが、データ分析に関しては素人に毛が生えた程度なので、
いい機会だと思って経験を積んでいる最中。

# 4.分析のKPIがハッキリしていない

まず、何のために分析をしているのかハッキリしていない。
というのも、弊社は開発と営業が完全に分かれているため、
開発側のKPIとしては、分析ができるようにするのが目的なのか、
それともそれを解約率を下げるための資料として使えるものにするのか、

とりあえずデータ分析やってみて、と投げられた一人のエンジニアの気持ちとしては、
何ができるようにしてほしいという目的が無いと、手段も結果も得られないのだが、
残念な事に社内で誰もデータ分析の知見がないため、迷走してしまっている。

# 6.終わりに

とりあえずデータ分析を始めて思っているのだが、データを残せという言葉が社内ではこのときのために言われてきたのだろうが、
いざ分析を始めたときに大して役に立たないデータばかり残っていて、逆に分析の邪魔になりかねないので、

本気でデータ分析を、未来でする気があるのであれば、
残すデータの形式、必要なデータと、不必要なデータを考えて、とりあえずデータベース全体残しておけば使えるっしょ、はマジで辞めてほしい。
AppleStoreでアプリのアップデートしようとしたら忘れてたこと

## はじめに

大したことじゃないんですが、久しぶりにアプリをアップデートしようとしたら少し待たないといけなかったことがあったので
こちらに書いておきます。
と言っても警告も出てたし普通気づくことなのですが。。（僕はダメでした）

## 有料Appの契約更新

自分は無料Appしかストアには出してないのですが、それでも納税フォームというのに書類を提出する必要があるようですというかあります。
そこで提出して更新しないと無料Appのバージョンアップもできないんですね。

iOSのアプリを更新するときはAppStoreConnectで
画像でいうところの左上の 「iOS App」の横の青いプラスボタンを押すと
新規バージョンのダイアログが出て作成に進むことができます。

![スクリーンショット 2021-02-01 1.31.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701264/6d84a2d4-f08b-6b69-6a65-2bc44d1db900.png)

画像をとっておけば良かったのですが、契約更新をしていないとこちらが契約の画面への遷移を促すダイアログに変わってそちらで提出をするまではアプリがアップデートできないようになっています。

## 最後に

個人で僕のように期限とかもなく気まぐれでアップデートや新規アップロードをする方は特に問題ないですが、
法人のアカウントでこの日にアップデートをしないといけないとか、保守しているアプリで期限内に提出しないといけないなど業務になってくると問題も出てくると思うので一応頭の片隅に置いておくと良いなーと思いました。
Djangoのadminページ（管理画面）がServer Error(500)で表示されない時の対処

以下の記事でまとめているDjangoで作成したアプリをHerokuへデプロイする時に躓いた問題を解消したあと、今度はDjangonの管理画面が表示されなくなってしまった。

アクセスしても↓のように表示されてしまう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230966/9ea618a3-5456-8a93-ce77-e33e0dcfd40f.png)

その時の解消方法を残しておきます。

[Django Girlsチュートリアルで Heroku へデプロイするときに躓いたこと -スタックに注意](https://qiita.com/yujimod/items/595a4ecba991b2e5ccd1)


#結論から言うと「静的ファイルを集める作業」で解消

以下のコマンドで静的ファイル（CSSファイルや画像ファイルなど変化しないもの）を集めて、利用できるようにするのだが、これを行ったところ解消した。


```
$ python manage.py collectstatic
```

Herokuへのデプロイで追加したWhitenoiseというモジュールが静的ファイルの扱いに関するモジュールのようなのですが、それで躓いていたのでその影響によるものかもしれません。
具体的なファイルは何だったのかわかりませんが、1つの事例として参考になればと思います。


#他に確認したこと

上記の解決以外に確認したことは以下のようなことです。自分の場合はいずれも問題なかったようです。

* インストールされているDjangoはインストールされているpythonに対応しているか

* （仮想環境を構築していたので）仮想環境を起動していたか

* `pip`ではなく`pipenv`でインストールする必要があるモジュールがいくつかあったので、`Django`も念の為再度やってみてた（関係なかったっぽい）


#今回学んだこと

Webサイト作成経験があったので、単にwebサイトを運用する上では、静的ファイルはもしなくてもリンクエラー程度でサイトそのものが動かないということはないとわかっていたのですが、Webアプリのようなものではそれでもサーバーエラーになってしまうんだな、と勉強になりました。

チュートリアルでもいろいろと環境が変わってくると、何がトリガーとなるかの判断が難しくなってきて、些細なことでも解決に何時間もかかってしまいますね。
（こうやって学んでいくのかな。。。）
RailsでBootstrapのグリッドシステムのみを使用する

# 経緯
現在関わっているプロジェクト内で、以下のような要件があった。
1. フロント側レスポンシブの対応を`Bootstrap`のグリッドシステムを使いたい。
2. 他のスタイルは`Bootstrap`を使わずにCSSを独自で実装したい

`Rails`のアプリケーションにBootstrapを導入する手順は多く公開されているが、`1.`のグリッドシステムのみ導入が分からず、少し困ったので備忘録として残しておく。

## 環境
- `Rails 6.0.3.4`
- `Ruby 2.7.2`

Rails6から、WebPakerを使ったJSの管理がデフォルトになったので、yarnでBootstrapを追加します。

```ruby
$ yarn add bootstrap
```

Bootstrapすべてインポートする場合

```ruby:app/assets/stylesheets/application.scss
# 省略
import "bootstrap/scss/bootstrap.scss"
```

グリッドシステムのみを抜き出したい場合

```ruby:app/assets/stylesheets/application.scss
# 省略
import "bootstrap/scss/bootstrap-grid.scss";
```

`node_modules`配下を確認するとの以下のパスに`bootstrap-grid.scss`が配置されている

```terminal
$ ll node_modules/bootstrap/scss/

省略

-rw-r--r--   1 koeda  staff    409 12  2 20:29 bootstrap-grid.scss
-rw-r--r--   1 koeda  staff    918 12  2 20:29 bootstrap.scss
```


また`bootstrap.scss`を確認すると以下のように、分割されたスタイルシートをインポートしているため、用途に合わせて、別途抜き出して使うこともできる。

```scss:node_modules/bootstrap/scss/bootstrap.scss 
/*!
 * Bootstrap v4.5.3 (https://getbootstrap.com/)
 * Copyright 2011-2020 The Bootstrap Authors
 * Copyright 2011-2020 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 */

@import "functions";
@import "variables";
@import "mixins";
@import "root";
@import "reboot";
@import "type";
@import "images";
@import "code";
@import "grid";
@import "tables";
@import "forms";
@import "buttons";
@import "transitions";
@import "dropdown";
@import "button-group";
@import "input-group";
@import "custom-forms";
@import "nav";
@import "navbar";
@import "card";
@import "breadcrumb";
@import "pagination";
@import "badge";
@import "jumbotron";
@import "alert";
@import "progress";
@import "media";
@import "list-group";
@import "close";
@import "toasts";
@import "modal";
@import "tooltip";
@import "popover";
@import "carousel";
@import "spinners";
@import "utilities";
@import "print";
```

#### 余談
`node_modules/bootstrap/scss/bootstrap.scss`を確認すると`_alert.scss`や`_reboot.scss`など、接頭語が`_(アンダースコア)`で始まるファイル名となっている。
これは、scssファイルをコンパイルしても`css`ファイルが生成されない仕様らしいです。
またインポートする際には`_(アンダースコア)`は省略できます。