OmniAICreator/Qiita-1.07M huggingface dataset:
短編映画「Hello, world!インセプション」

# 第1階層 Windows

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/8c0b09a0-1caa-1401-28a4-a5b581366062.png)

ファイル名を指定して実行

`cmd`

```
Microsoft Windows [Version 10.0.19042.630]
(c) 2020 Microsoft Corporation. All rights reserved.

C:\Users\user>
```

`>echo "Hello, world!"`

```
C:\Users\user>echo "Hello, world!"
"Hello, world!"
```

# 第2階層 Ubuntu (WSL)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/ea52eef2-b512-176d-3cc3-29128b62a4ee.png)


`>wsl`

```
C:\Users\user>wsl
user@DESKTOP-EPGPMTG:/mnt/wsl/docker-desktop-bind-mounts/Ubuntu-20.04/7272****$
```

`$ echo "Hello, world!"`

```
user@DESKTOP-EPGPMTG:/mnt/wsl/docker-desktop-bind-mounts/Ubuntu-20.04/7272****$ echo "Hello, world!"
Hello, world!
```

# 第3階層 Amazon Linux (EC2)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/48da8f02-d9f0-8521-616b-656975a1aa85.png)

`$ ssh -i "****.pem" ec2-user@ec2-****.us-east-2.compute.amazonaws.com`

```

user@DESKTOP-EPGPMTG:~$ ssh -i "****.pem" ec2-user@ec2-****.us-east-2.compute.amazonaws.com
Last login: *** ***  * **:**:** 2020 from ****.**.**

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-**** ~]$
````

`$ echo Hello, world!`

```
[ec2-user@ip-**** ~]$ echo Hello, world!
Hello, world!
```

# 第4階層 CentOS (Docker)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/72a29790-6da0-bd4f-975e-cebb3389ee48.png)

`$ docker run -it centos /bin/bash`

```
[ec2-user@ip-**** ~]$ docker run -it centos /bin/bash
[root@**** /]#
```

`# echo "Hello, world!"`

```
[root@**** /]# echo "Hello, world!"
Hello, world!
```

# 第5階層 Python

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/8958baf0-5cc4-5d94-1794-4c57a4de0824.png)

`# yum install python3`
`# python3`

```
[root@**** /]# yum install python3
[root@**** /]# python3
Python 3.6.8 (default, Apr 16 2020, 01:36:27)
[GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

`>>> print ("Hello, world!")`

```
>>> print ("Hello, world!")
Hello, world!
```

# 脱出

```
>>> exit()
[root@**** /]# exit
exit
[ec2-user@ip-**** ~]$ exit
logout
Connection to ec2-****.us-east-2.compute.amazonaws.com closed.
user@DESKTOP-EPGPMTG:~$ exit
logout

C:\Users\user>exit
```

# エピローグ

　実際の映画（？）ではここで謎の男が出てきてexitと入力すると主人公の存在が消えてしまいます。

　あと、一点つまづいた点として、wslがWindowsディレクトリのままいるとssh接続する時にPermissionエラーが出てしまうので、WSLのフォルダに入れた後に`chmod 400`しないといけませんでした。WSLからssh接続する時に`Load key "****.pem": bad permissions. Permission denied (publickey,gssapi-keyex,gssapi-with-mic).`というエラーが出てくる時は[参考ページ](https://qiita.com/yoshioms/items/6e2039d58fe19ab61b5f)の通りにやると上手くいきます（検索用要約）。
Mysql入門【授業編】XAMPP ver

<h1>XAMPPからmysqlにつないで、DB作成、テーブル作成の流れをやる</h1>

<h3>①XAMPP起動</h3>
startボタンを押す

<h3>②ターミナルに繋いで、mysql起動</h3>
open terminalをクリック。
その後、ターミナルが開いたら
```mysql```

そして、```MariaDB [(none)]>```が表示されたら、オッケー。
<h3>③データベースに繋ぐ</h3>
既存のDBに繋ぐ。
```use データベース名```

<h3>④確認系のコマンド</h3>
今あるテーブルの一覧を表示
```show tables;```
+---------------+
| Tables_in_saku |
+---------------+
| country       |
| mhold         |
| mount         |
| rhold         |
| river         |
+---------------+

こんな感じで出てくる

<h3>⑤テーブル作る</h3>
```
create table sns (
id serial primary key,
name text,
title text,
message text not null,
date date not null,
password text
);
```

それからshow tables;で確認するとsnsが追加されているはず！

<h3>⑤テーブルの確認（詳細）</h3>
テーブルの詳細確認は、
```describe テーブル名;```
```desc テーブル名;```でも可能


+----------+---------------------+------+-----+---------+----------------+
| Field    | Type                | Null | Key | Default | Extra          |
+----------+---------------------+------+-----+---------+----------------+
| id       | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name     | text                | YES  |     | NULL    |                |
| title    | text                | YES  |     | NULL    |                |
| message  | text                | NO   |     | NULL    |                |
| date     | date                | NO   |     | NULL    |                |
| password | text                | YES  |     | NULL    |                |
+----------+---------------------+------+-----+---------+----------------+
こんな感じ
UITableViewでSwipeを提供する際にはcompletionHandlerを呼ぼう

`UICollectionView`ベース実装の時代にあれですがTipsです。

```swift
tableView(_ tableView:trailingSwipeActionsConfigurationForRowAt: IndexPath) -> UISwipeActionsConfiguration?

func tableView(_ tableView:leadingSwipeActionsConfigurationForRowAt: IndexPath) -> UISwipeActionsConfiguration?
```

上記の2つは現在スワイプアクションを提供するための唯一の公式APIですが、公式ドキュメントの解説が結構少なくて落とし穴があります。

今回の記事で紹介するのは、その落とし穴の1つと対処法です。

# UIContextualActionが最新にならない
以下のような、何らかの条件で表示するアクションが変わるように実装していたとします。ですが、**これでは「アクションが最新にならない」**という問題に遭遇することがあります。

```swift
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

        let action = UIContextualAction(style: .normal,
                                              title: nil) { [weak self] (_, _,_) in
            // Something to do
        }

        guard yourStatement else {
            return .init(actions: [action])
        }

        let secondAction = UIContextualAction(style: .normal,
                                               title: nil) { [weak self] (_, _,_) in
           // Something to do
        }

        return .init(actions: [action, secondAction])
    }

```

# 原因
ネット記事などを見ても上記のようなコードはよく見かけますが、`UIContextualAction`のクロージャーの3番目のパラメータである[UIContextualAction.Handler](https://developer.apple.com/documentation/uikit/uicontextualaction/handler)を呼んでいません。

このhanderには以下のような説明があり、指定してくれとは書いてありますが、呼ばなかった場合にどうなるかの解説はありません。

> completionHandler
アクションを実行した後に実行するためのハンドラブロックです。このブロックは戻り値を持たず、以下のパラメータを取ります。
actionPerformed
アクションを実行したかどうかを示すブール値。アクションを実行した場合は true、何らかの理由でアクションを実行できなかった場合は false を指定します。

いくつかコードを動作されてみた限りの推測ですが、`completionHandler`にBool値をつけて呼ぶことで`UITableView`はスワイプが完了したことを知り何らかの完了・更新処理を内部的に実行するため、これを呼ばないと不整合が発生してテーブルに意図しない挙動が発生するようです。

# 対処後のコード
対処後のコードではパラメーターで`completionHandler`をとり、何らかの処理が終わった後に`completionHandler(true)`として実行しています。

```swift
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

        let action = UIContextualAction(style: .normal,
                                              title: nil) { [weak self] (_, _, completionHandler) in
            // Something to do
            completionHandler(true)
        }

        guard yourStatement else {
            return .init(actions: [action])
        }

        let secondAction = UIContextualAction(style: .normal,
                                               title: nil) { [weak self] (_, _, completionHandler) in
            // Something to do
            completionHandler(true)
        }

        return .init(actions: [action, secondAction])
    }

```

# まとめ
たった2行ほどの処理ですが、`completionHandler(true or false)`の実装有無に気づかないだけで数時間を費やしてしまうことがないように、この記事が助けになれば良いかなと思っております。
S3のバケットの一部のフォルダの中のファイルを公開する

## 概要

アプリケーションを作成していて，S3に画像を置いて公開したかった．

既にユーザーからの入力などを保存しておく非公開のバケットを作成していた．

画像を配信するには公開用のバケットを別途作成しなければいけないかと思っていたが，バケットの一部分のファイルのみを公開することができるらしい

これにより1つのアプリケーションで複数のバケットを使用せずに済んだ．
また複数のバケットを作成，管理する必要がなくなった．

手順をメモしておく



## やったこと

バケットの一部のフォルダの中に入っているファイルのみパブリックアクセス可能にした．

その他のファイルは全て非公開になっている．



## やり方

1. 公開したいバケットのページにいき，アクセス許可からブロックパブリックアクセスを編集
    デフォルトではパブリックアクセスを全てブロックにチェックがついているが，これを外して4つの項目のうち下2つのチェックを外した状態で変更の保存をする．
![スクリーンショット 2020-12-08 18.27.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401124/b2be46c5-40ba-2da3-05f1-385563badafc.png)


2. バケットポリシーを作成する
   バケットのページのアクセス許可からバケットポリシーを編集する
   バケットポリシーには以下のjsonを記入する

   ```json
   {
     "Version":"2012-10-17",
     "Statement":[
       {
         "Sid":"AddPerm",
         "Effect":"Allow",
         "Principal": "*",
         "Action":["s3:GetObject"],
         "Resource":["arn:aws:s3:::DOC-EXAMPLE-BUCKET/publicprefix/*"]
         }
     ]
   }
   ```

   ここで`DOC-EXAMPLE-BUCKET`にはバケットの名前，`publicprefix`には公開したいフォルダの名前を書く



以上で一部のフォルダのみを公開することができる．

試しに公開設定をしたフォルダの中のオブジェクトのオブジェクトURL（https://bucket-name.s3-ap-northeast-1.amazonaws.com/hoge/hoge.png のようなurl．各オブジェクトのページに記載されている）を開くと公開されていることがわかる．

また公開設定していないフォルダの中のオブジェクトのオブジェクトURLを開くとアクセス拒否されるのでこちらはきちんと非公開になっていることがわかる．



## 参考

AWS公式

https://aws.amazon.com/jp/premiumsupport/knowledge-center/read-access-objects-s3-bucket/

こちらのページにはフォルダ以下を公開するやり方以外にもオブジェクトにタグをつけて特定のタグがついているオブジェクトのみ公開する方法なども紹介されている．
Heroku Postgresを psql 以外で扱う。

# Heroku Postgresデータへのアクセス
Herokuの標準的なデータベースは、説明する必要も無く、PostgreSQLですね。Herokuでのプロビジョニングはまったく難しい事無く、単にプランを選択するだけどいうのは、私含めてHerokuラヴの皆様であれば、知らないわけがないはずです。そこまでは、超フレンドリーなHerokuですが、そこから先は「漢」の世界。CLIで、

`` heroku pg:psql --app [アプリケーション名]
``
での利用を求められます。もちろん、データベースの資格情報（Credentials)を使って、pgAdmin含め、星の数ほどあるPostgreSQLの管理ツールの利用もできますが、資格情報の管理も結構面倒です。ということで、他の方法をご紹介します。

## Dataclipsを使う
最初に思いつくのは、HerokuのDATAダッシュボード内にあるDataclipsです。Dataclipsは、クエリーを定型化しておいて、簡単に実行できるようにするツールでなので、インタラクティブにデータにアクセスすることを目的としていないですし、いわゆるGUI的なツールではないですが、コード補完機能を備えたエディタでのSQL構文の記述が可能で、データにアクセスできます。定型化することを前提にしているので、都度、Dataclipに名前を付ける必要があるのが面倒といえば、面倒ですが、それさえ気にしなければ、コード補完してくれるため、psqlよりはかなり便利です。気をつける点としては、データ参照はできますが、INSERTやUPDATEはできないところです。まぁ、このあたりは、Dataclipsの目的としては仕方ないところです。
![スクリーンショット 2020-12-08 16.04.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/eca463b2-f3bd-e5a6-327e-0d10bad8d33e.png)


## Heroku Add-onを使う
こちらが本命ですね。いくつかAdd-onがあるのでご紹介します。紹介するAdd-onは、データベースの資格情報の入力無しに利用可能なのが便利なところです。(Add-onなので当たり前って言っちゃー当たり前そうですが）。ということで簡単にご紹介します。
### Adminium
https://elements.heroku.com/addons/adminium
Dataclipと違い、新規レコードの作成や編集・削除ができる。CSVの**インポート**・エクスポートも可能可能。サービス名にも現れているように、主に管理者向けツール。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/80f6ccc6-b343-5e3a-a6a9-086afc1e8913.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/7735189d-5884-f73c-9062-ddf7319b78f4.png) 
### Draxlr for Postgres / MySQL
https://elements.heroku.com/addons/draxlr
こちらも管理者向けツールという位置づけで、新規レコードの作成・編集・削除ができる。グラフも豊富。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/28140bec-3087-a6e2-0c12-c875082f2d2f.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/a5ebf88f-1214-6066-4790-215fd4460126.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/fd13ffff-f7de-63d7-87e6-7912de1807b7.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/3136a9fb-f161-198c-7bb2-38519dacc018.png)
### Trevor.io for Postgres / MySQL
https://elements.heroku.com/addons/trevor 
管理者向けというよりは、Dataclipに近く、データを可視化することを主な利用目的になっており、ユーザー向けダッシュボード構築に向く。Dataclips同様、Google Spreadsheet等の連携により、Herokuにアクセスしなくても、データ参照できる。クエリー作成もビジュアルライズされていて、SQL構文で記述する必要が無いのは便利（ただし、当然ながら、SQL構文は意識させる）。スキーマのビジュアライズもでき、グラフ種類も豊富。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/c36a008b-914e-65a0-2343-b610abf014e3.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/839db001-793d-813b-ac34-637bc1794db9.png) ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3816/6f47b6a7-6d57-7881-fae9-a12c941b27ec.png)

##注意点
Private Spacesでの利用は当然ながら制限があります。一部Add-onについては、**trusted IPs for data**を使って、Private Spaces外部からのPostgresへのアクセスを許可すると利用できます。また、Private Spaces対応を謳ってないAdd-onもあるので、注意が必要です。
## まとめ
各Add-onで価格も大きく異なっていますが、どれも無料プランが用意されているので、まずは、お試しで使ってみると良いかもしれません。
[Rails]ransackでの月度検索の実装(Custom Predicates)

# 概要
ransackを使用している際、月度検索を使用したくなったので以下の通り実装した。

↓例：2020年12月を選択したら、2020/12/1-2020/12/31まで検索する
[![Image from Gyazo](https://i.gyazo.com/dba3b564b9eb69ecab93c5f79478ca9c.png)](https://gyazo.com/dba3b564b9eb69ecab93c5f79478ca9c)

# 実装
検索用の述語(Predicate)を作成する。
add_predicateで述語を追加、述語名は「during_month」で実装。

```
# config/initializers/ransack.rb

Ransack.configure do |config|
  config.add_predicate :during_month,
                        :arel_predicate => :between,
                        :formatter => proc { |v|
                          unless v.month == 12 # 12月度以外の処理
                            #月末日をenddayに代入 v.monthを+1して最後にdateを1引くことで月末日を取得している。
                            endday = Time.zone.parse("#{v.year}-#{v.month + 1}-1").to_date - 1
                          else # 12月度の処理
                            # 12月度の時はv.monthはインクリメントしない（エラー回避）
                            # v.yearを+1で翌年に、monthは1月固定、
                            # 最後に1日に固定したdateを1引くことで12月の末日をenddayに代入している。
                            endday = Time.zone.parse("#{v.year + 1}-1-1").to_date - 1
                          end
                          #検索月の初日から月末日まで
                          Time.zone.parse("#{v.year}-#{v.month}-1").to_date..endday
                        },
                        :type => :date
end
```

View側の実装

```
<%= search_form_for "検索オブジェクト", url: "アクション" do |f| %>
  <%= f.label :search_day, "期間"%>
  <%= raw sprintf(
      f.date_select(
        :search_day_during_month,
        discard_day: true,
        use_month_numbers: true,
        date_separator: '%s'),
      '年 ') + '月' %>
  <div class="actions ransack-submit"><%= f.submit "検索" %></div>
<% end %>
```

# 参考
ransackで年度検索を実装する
https://qiita.com/tanakaworld/items/c7a2613589dadbb2ef4d

Custom Predicates · activerecord-hackery/ransack Wiki
https://github.com/activerecord-hackery/ransack/wiki/Custom-Predicates
neo4jでsandbox その19

#概要

neo4jでsandboxやってみた。
mitre att&ckを投入してみた。

#blankで、クリエイト　プロジェクトする。

アドレスとパスワードを控える。
graph_bolt = "bolt://100.26.171.12:34013"
graph_auth = ("neo4j", "cable-vomit-preserver")

#githubより、mitre-enterprise-attack.jsonを落とす。

https://github.com/mitre/cti/blob/master/enterprise-attack/enterprise-attack.json

#githubより、attack2neo.pyを落とす。

https://github.com/vmapps/attack2neo

#sandoboxに、投入する。

python attack2neo.py --debug -f enterprise-attack.json --groups --softwares --tools --relations

#plunkerで呼び出す。


```
MATCH (g:Group {name:"APT18"}) 
WITH g MATCH (g) - [:alias] -> (a:Alias) 
RETURN g.name AS Name, a AS Alias
```

|Name|	Alias|
|:--|:--|
|APT18|	(16:Group:Alias {name:"Threat Group-0416",type:"intrusion-set"})|
|APT18|	(15:Group:Alias {name:"Dynamite Panda",type:"intrusion-set"})|
|APT18|	(14:Group:Alias {name:"TG-0416",type:"intrusion-set"})|


#成果物

http://embed.plnkr.co/plunk/OeHv09C1TCLFWLxQ

以上。
Chrome DevTools(デベロッパーツール)の役立つ便利機能

こちらの記事は、PARONYM Advent Calendar 2020 - Qiita の8日目です。

実はQiita初投稿です。デビューしちゃいました。
やれているかはおいといて、できるだけ理解り易い記事になるように記載していきます。

初投稿は以下の内容で書いていこうと思います。

##Chrome DevTools(デベロッパーツール)の役立つ便利機能

- [Chrome DevTools公式](https://developers.google.com/web/tools/chrome-devtools/?hl=ja)

Web開発、特にフロントエンド開発には必須の「Chrome DevTools(デベロッパーツール)」ですが、その多機能ぶりと便利さにはとても驚きます。

もっと早く知りたかった、それは前々から知ってた...とか多々あるかと思いますが、温かい目で見守って下さい。

###1.CSSの調整
これは超絶定番ですが、CSSの調整をその場で確認・調整ができます。
element.style{ }内に、CSSを埋め込むとページでは直ぐに反映されるので、その反映結果を見ながら好ましいスタイルに調整できます。完了したらそれをそのままCSSファイルに組めこめばOKですね。
なお、paddingやmarginなど数値を扱うものは数値をクリックして選択した状態で、矢印キーで微調整できのも結構嬉しいです。
![スクリーンショット 2020-12-08 16.06.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912565/82c2b6e1-fd0f-1cdf-0a10-ce0a2b4e783e.png)

###2.JavaScriptのデバッグ
JavaScriptのデバッグではElementsタブ内のConsoleもしくは、Consoleタブ内でデバッグするのが一般的かと思いますが、Sourcesタブ内でもデバッグができます。

![スクリーンショット 2020-12-08 16.30.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912565/59daec08-f79e-32b3-d47e-1a7d0296872b.png)

JavaScriptのソース行をクリックすることで青色になり、ブレイクポイントを設定することができます。
これを設定した状態で、何らかのメソッドが実行されるように動作させると、ブレイクポイントで動作が一旦止まります。この時に、例えば宣言済みの変数にマウスカーソルを合わせると、オブジェクトプロパティ一覧が表示されます。
なので、処理を行いたい変数がメソッド等処理が走った際に、どのような中身になっているかをこれで確認することができます。思わぬタイミングで意図していない動作になった時の確認に便利です。
また、一つ一つconsole.logでデバッグせずに、ブレイクポイントから中身を確認することもできるので、手間も省けます。

###3.キャッシュの自動削除
ブラウザのキャッシュに踊らされたことはありませんか？
キャッシュの削除には、更新ボタンの右クリックを行い、「ハード再読み込み」または「キャッシュの消去とハード再読み込み」で行うことができますが、DevToolsのNetworkタブ内にある「Disabled cache」にチェックを入れることでもDevToolsを開いている間、自動で削除することができます。これでCSSが反映されていないといった症状とはおさらばです。

![スクリーンショット 2020-12-08 16.50.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912565/2e6e128c-b763-2cb8-bbbd-ce31e94deb97.png)

###4.ヘッダー情報やリクエスト情報の確認
こちらはどちらかというとバックエンド開発時の話しになりますが、POSTしたりGETしたりする際に、どのURL先にどんな各種ヘッダーを付与して送信しているか、またその通信結果はどうなのかといった情報を確認することができます。
クッキー情報だったり、キャッシュ情報だったり、Formタグからどんな内容を送信しているかといった内容を確認できます。
たまに使います。

![スクリーンショット 2020-12-08 17.12.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912565/cd4ee2e7-8f7f-1a6e-ecff-887cb20450ed.png)

###5.z-indexの位置付け確認
z-indexの前後関係を確認するツールもあるようです。
DevTool内で「command + Shift + P」を押して検索ウィンドウが表示されたら「layer」と入力します。
選択するとlayerタブが新たに追加されますので、以下の画像のようにどの要素が上に来ているかを視覚的に確認することができます。z-indexが増えてきた時に便利です。

![スクリーンショット 2020-12-08 18.12.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912565/f0918ff4-282d-34bd-6697-df9fbd83a5b6.png)

###6.Minify化されたJavaScript, CSSの整形
MinifyされたJavaScriptやCSSを見たことがあると思いますが、DevToolsを利用することでこれを普段見るような形式に整形することができます。どうやって動作しているのか確認・参考にしたい時に利用できて便利だと思います！


![スクリーンショット 2020-12-08 18.21.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912565/b9b34161-9cf3-63aa-a11b-249f7343ab6d.png)

##まとめ
DevToolの便利機能をざっくりまとめました。

- CSSの調整
- JavaScriptのデバッグ
- キャッシュの自動削除
- ヘッダー情報やリクエスト情報の確認
- z-indexの位置付け確認
- Minify化されたJavaScript, CSSの整形

もし知らない方がいらっしゃったら、利用してみてください！

以上
Reactアプリ作成時にエラー"'--jsx' フラグが指定されていないと、JSX を使用できません。"が発生する場合の対処

現在

```
npx create-react-app hello-world --template typescript
```

上記コマンドで作成したプロジェクトをVSCode等で開くと、
各種tsxファイルにおいて

`'--jsx' フラグが指定されていないと、JSX を使用できません。`

上記エラーが発生する場合があります。
これは使用しているTypescriptのバージョンが古いことに起因するものです。

#解決方法
ワークスペースのTypescriptを使用しましょう。
以下VSCodeの例

①VSCode右下のTypescriptのバージョンが表示されている箇所をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663118/e5a83245-62ea-ac60-d01f-cf27995d82c2.png)

②Typescriptのバージョンを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663118/8aaf001a-fe39-00ca-8d64-6be7a58529f7.png)

③ワークスペースのバージョンを使用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663118/d9b1fb0c-9b80-bba2-71f1-55110931eb71.png)

④VSCode再起動

以上でエラーは出なくなっているはずです。
お困りの方の手助けになれば幸いです。
新規開発ノウハウのデータベース化

[QualiArts Advent Calendar 2020](https://qiita.com/advent-calendar/2020/qualiarts)、10日目の記事です。
QualiArtsで開発ディレクター（以下、開発D）をしている、新見です。去年は[開発ディレクターとしての役割](https://qiita.com/Koheinimi/items/4277c308fccf02056642)という記事を書かせていただきました。
今年は、開発Dのメンバーでここ数年取り組んできた、新規開発のノウハウを蓄積するために実施している取り組みについて書きたいと思います。

#背景
ゲーム開発の規模が拡大する一方で、社内にまとまった開発のノウハウ（開発を進行する上で必要な情報）はありませんでした。
個人の知見としては蓄積されていたので、都度、その分野に詳しい人にヒアリングをしたり、社内のwikiを検索すれば、どこかでキーワードが引っかかるといった状態はよくありました。
過去にいくつも新規サービスの開発を経験してきているのに、そのノウハウがまとまっていない状態は非常に勿体無い為、次の新規開発では情報をまとめていこうとなったのが、この施策の始まりです。

#目的と進め方
###目的
QualiArtsの開発ノウハウをデータベース化し、新規のネイティブプロジェクト開発をする際に開発Dが道標にできるようにすること。
###進め方
各開発フェーズで、開発を進める上で必要な情報をリアルタイムでwikiにドキュメントとしてまとめていく。
定例で進捗を共有。

#進める上でのポイント
この取り組みはノウハウのデータベース化と言うだけあり、新規開発の立ち上げからリリースまでの長期間、継続して取り組む必要があります。まとめる上でどこまで詳細にまとめていく必要があるのか？社内全体に公開するのか？公開する場合は他職種のことを考慮しなければならない。など、進める上で考えなくてはならないポイントがありますが、最初に方針を定めました。

- 詳細を細かく記載するのではなく要点をまとめ、各プロジェクトの資料等へリンクを飛ばす等により、極力工数をかけず、情報が古くならないことに重きを置く。
- 開発Dの参考になればOK。
- 他職種の利用は考えない。
- あくまでも、マニュアルではなく参考情報とする。
- 参考にした上で開発D自らが判断し動くこと。

極力工数をかけずに実施したこともあり、忙しい中でも継続してこの施策を進めることができたのかなと個人的に感じています。

#開発フェーズと項目の紹介
具体的にどのような項目でまとめているのかを、一部紹介していこうと思います。
**01.本開発前**
　┣ 01.01.全体ロードマップ
　┣ 01.02.各種チェック点
　┣ 01.03.各種ツール
　┣ 01.04.技術選定
　┣ 01.05.ゲーム仕様
　┣ 01.06.3D仕様
　┣ 01.07.体制
　┣ 01.08.工数見積もり
　┣ 01.09.モック開発

**02.本開発**
　┣ 02.01.開発フロー
　┣ 02.02.開発バージョン
　┣ 02.03.進捗管理
　┣ 02.04.ブラッシュアップ
　┣ 02.05.プロモーション
　┣ 02.06.アカウント申請関連

上記は一部ではありますが、他にも、**03.検証**や**04.リリース前**など、各フェーズで必要な情報をまとめています。ページ内には各プロジェクトごとのそれぞれの情報がまとまっています。

#1周目をまとめ終え、施策実施のよかったポイント
1. 開発D全員に新規立ち上げのノウハウを共有できたことです。参加者全員が立ち上げからリリースまでの知見を得られる事ができました。
2. 当事者意識を生む事です。先行しているプロジェクトがタイムリーにノウハウを共有することで後発プロジェクトが事前に準備することができました。
3. リアルタイムで課題の相談ができる事です。今起きている課題を一度整理した上で相談できるので、新規開発で発生する課題を乗り越えることができました。

#まとめ
現在、私自身、新規開発の真っ只中ではありますが、このノウハウを参考にすることは頻繁にあります。ドキュメントとしてまとめてよかったなと実感しています。開発フローなどはその都度アップデートはされていくものなので、ドキュメントの状態も古いままにならないよう今後も継続して運用していきたいと考えています。
最後まで読んでいただいてありがとうございました。
Mattermostでいきなり504エラーが出てきだしたと思ったら、ファイルディスクリプター不足だった話

毎度、ググっても出てこない小ネタを取り扱っております。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。

# 0. はじめに

社内のMattermostを運用し始めてからそろそろ2年ですが、快適に使えておりました。
しかし、突然 504エラーが発生しはじめたのでございます。

```shell
10.XX.YY.ZZ - - [19/Oct/2020:05:16:03 +0900] "GET /api/v4/users/me/teams/g8wt6k198p87fb5t69n4skknqc/channels/members HTTP/2.0" 504 569 "-" "k3xaqkwfefgxdnkcpd1gupi83o" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Mattermost/4.5.3 Chrome/78.0.3904.130 Electron/7.3.2 Safari/537.36" "-" "61.503"
10.XX.YY.ZZ  - - [19/Oct/2020:05:16:03 +0900] "GET /api/v4/users/me/teams/g8wt6k198p87fb5t69n4skknqc/channels?include_deleted=true HTTP/2.0" 504 569 "-" "k3xaqkwfefgxdnkcpd1gupi83o" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Mattermost/4.5.3 Chrome/78.0.3904.130 Electron/7.3.2 Safari/537.36" "-" "61.504"
```

リソースには余裕があり、メモリー/CPUの問題ではありませんでした。

しかし、Mattermostのログを見ると以下のログが大量に出力されておりました。

```
{"level":"error","ts":1603052014.4308474,"caller":"http/server.go:3088","msg":"http: Accept error: accept tcp [::]:8065: accept4: too many open files; retrying in 10ms","source":"httpserver"}
```

見事にサボっていたファイルディスクリプターに引っかかっておりました。

# 1. ulimitを増やす

一番簡単なのがsystemdのserviceを直す事でしょう。

以下を参考にしました。
https://github.com/mattermost/mattermost-docker-preview/issues/32#issuecomment-405083050

```
sudo vi /etc/systemd/system/mattermost.service
```

以下の下線部分を追記します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/d398e68b-e897-f99b-85c0-02411060949b.png)


```bash
LimitNOFILE=65535
```

`TimeoutStartSec=3600` も追加する人もいるようです。

systemdをリロードします。

```
sudo systemctl daemon-reload
```

# 2. まとめ

反省.
手を抜くといかんですね...。

その他にも `/etc/sysctl.conf` も直すと良いかもです。

High Availability Cluster (E20) — Mattermost 5.29 documentation
https://docs.mattermost.com/deployment/cluster.html#mattermost-server-configuration
リモートでクラッカーを鳴らしてみる


この記事は  [IoTLT Advent Calendar 2020 ](https://qiita.com/advent-calendar/2020/iotlt)の9日目の記事です

ちょっとまえにクラッカーを鳴らすLTをしたので、記事にしてみます


### 最近の悩み

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227015/fddc79b9-4e7d-51f8-cc98-cb1230e2a288.png)

そんなちょっとした変化ですが、登壇をしていると寂しくなることが多く感じるようになりました。
でもこれって登壇以外のときはそんなに変化を感じないんですよね。LINEしててもSlackしててもそんなに感じない。

なんでかなと考えていたら、これのおかげじゃないかと思うものを見つけました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227015/4c99ad26-4081-553d-9820-9bb91027516e.png)

そう、絵文字コミュニケーションが、気軽でみんな使うからもしかしてそんなに寂しくないのでは？？
でもリアルタイムの登壇で絵文字コミュニケーションって取りづらいですよね。というわけで、登壇中でも絵文字が来たら気付けるようにしてみたらどうでしょう。

## というわけで作ったもの

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227015/a3e33717-d632-6a8b-ef5b-0537b06290e0.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227015/a1fc5d54-2336-a93e-ea01-3e7da6d3d0e8.png)


登壇中にTwitterでクラッカーを見つけたら音を鳴らします

### ソフトウェア

```javascript

const Obniz = require("obniz")
console.log("start");
const obniz = new Obniz("xxxxxxxx");
var Twitter = require('twitter');
var client = new Twitter({
  consumer_key: 'xxxxxxxx',
  consumer_secret: 'xxxxxxxx',
  access_token_key: 'xxxxxxxx',
  access_token_secret: 'xxxxxxxx'
});

let count = 0;

obniz.onconnect = async () => {
  console.log("obniz connected");
  const motor = obniz.wired("DCMotor", {forward: 1, back: 0});
  motor.power(0);
  motor.move(true);
  obniz.repeat(async () => {
    if (count >= 10) {
      obniz.repeat(() => {
      });
      for (let p = 40; p <= 100; p += 10) {
        console.log(p);
        motor.power(p);
        await obniz.wait(2000);
      }
    }
  }, 2000)
};

client.stream('statuses/filter', {track: '🎉'},  function(stream) {
  stream.on('data', function(tweet) {
    count++;
    console.log(count);
  });
});
```

twitter SDKの`client.stream`で検索し、🎉をみつけたらカウントアップ、
obniz側ではそのカウントをみて10個以上ならモーターを回すようにしています。

最初から全力だと風情が無いので、スロースタートするようにしています。

### ハードウェア

早速チャレンジ！
![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227015/53f9dce3-6afe-cbdf-19d4-b4026b03de09.gif)


・・・力が足りずに鳴らすことができませんでした。


力が足りないならばと思って２つにしたら成功です

![out2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227015/a7508530-9f3d-8ba2-6ba6-efda6d4b1d00.gif)



### 学び

クラッカーを鳴らしたければミニ四駆モーター２つ必要です
IT初心者が入社1年目で社内システムのプロジェクトオーナーに（いつのまにか）なったときに気を付けていたこと

今回はせっかく機会をいただいたので、新たな業界に挑戦してる人でも活躍できるためのヒントになるような仕事の上でのポイントを書けたらなと・・・
IT初心者が入社1年目で初めてプロジェクトオーナーとして社内システム作成に携わった際に気を付けた事を書いていこうと思います。

初投稿なので生暖かい目でご覧いただけますと幸いです:shamrock:

#雑な経歴紹介
- 大学は微生物学専攻。（ずっと実験してたのでレポート書く時くらいしかPC触りませんでした）
- よくわからないまま受けて受かってしまったIT企業である弊社に入社。（内定後に企業研究しました。ごめんなさい。）
- 研修後よくわからないままSREに配属される。（何してるか説明されないままググってみてね！で配属されたのはいい思い出・・・）
- ひたすら黒い画面（Tera Term）と向き合う研修を経て、インフラ運用保守・監査対応をこなしながら今に至る。

#プロジェクトで実施したこと
- 機能構想の作成
- 実装の進捗管理
- テスト
- 運用

実装はCSSいじったりとか細かいところしかやってませんでしたが、他は一通りやりました。


#私が気を付けてたこと
###①目的を明確にする
ツールを作る際に便利に作ろうとして当初の目的からずれていく可能性があるので必ずぶれないようにしておくとよいと思います。
しかも実装が進むごとに迷子になりがちです。
必ず明文化しておきましょう！
そして定期的に見返すことが重要です。
弊社ではカタログという文化がありますが本当にこれがあってよかったなと感じた瞬間ですね・・・

###②取捨選択をする
機能構想してレビューいただいたときにいろんな人からあれやこれやと、要望を必ずいただきます。
しかも、ものすごい勢いで！
そういった時に一見聞いたときにいいなと思った機能であっても、持ち帰ってしっかりと原点に返って、本当に必要な機能か？どうかはしっかりと吟味しましょう。
この本当に必要な機能か？というところは①がしっかりしていればできるはずです！
たとえ上司から言われてたとしてもNoと言える勇気と根拠を持ちましょう～

***人間関係を円滑に進めるポイント!***
Noというときは必ず代替手段をもっていくとGoodですよ。
既存のツールでできるのではないか？別の人がやっているプロジェクトの一部にした方が有用のではないのか？などなど・・・
必ずどこかに突破口はあるはずです。~~（わからなければ先輩を使え！）~~

###③優先順位を決める
実装する。仕事をする。どんなことでも、やるべきことはたくさんあります。
進めていく中でトラブルはつきものですし、遅延だってあり得ます。
（実際携わったプロジェクトは当初の予定より遅延しました・・・）
そんな時どれを落とすかを決めるのは難しいですよね・・・そこでプロジェクト中に私が考えていた軸を共有します。

1. コアとなる機能（これがないとツールの意味がないレベルのもの）
2. コア機能を効果的に使うための機能（事故防止系）
3. 利用者にとって便利な付属機能 
4. 開発者にとって便利な付属機能

またこの優先順位は定期的に見直ししてアップデートされていく感じですね～
ただ、これはあくまですごく大きな枠組みなので柔軟な対応が求められたりします。
例えば
A.「開発者にとって便利な付属機能」が10分で開発できます！
B.「利用者にとって便利な付属機能」は5日で開発できます！
といった時に、どちらを優先する？となりますよね。
上の枠組みではBを優先したくなりますが、この場合はAを優先したほうがよさそうですね。

Aを先に開発した場合 → Aの開発に10分 + Bの開発に3日（でできるかも）  = 3日10分
Bを先に開発した場合 → Bの開発に5日 +　Aの開発に10分　　　　　　　   = 5日10分

つまり・・・結果としてBを先に開発したほうが作業工数が2日も余裕ができるかもしれないのです！
ですので、開発者からこの機能追加してよいか？と聞かれた際には必ず工数も聞くようにして費用対効果きちんと測りましょう！
~~（あといい息抜きになるみたいなので。コミュニケーション大切）~~

###④定期的な進捗確認と情報のアップデート
これは①や③の中でも触れていますが、どこまで何ができているか？実装順位に変更はないか？をしっかり確認して情報をアップデートしていきましょう。
進めていく中で新しく実装したい機能が出てくるかもしれません。
そこもしっかりヒアリングしてあくまで開発者が開発しやすいように進めていくべきです。
__た　だ　し！__
コアとなる機能だけは絶対に譲ってはいけません！できるかどうかではなく、やるしかなければやるだけです！
（ここで注意なのは機能の実装__”方法にこだわりを持たず”__、何が__”本当の目的”__かを見失わないように・・・）
弊社ではこれができている人がすごく多くて、勉強になるとともに働きやすいなーと感じていますね:relaxed:

###⑤既存のノウハウを使い倒せ
社会人になると学ぶ機会がすごく減る気がします・・・自分一人でできるやーって思うことでも一度学びなおしてみると新たな視点がでてきておススメです！
機能を考えるときはグーグルなどで機能として付けるならどういものが求められるのかを検索してみる。
モックを作る際はすでに世の中にある似たツールとかを見てみて使用感をつかむ。
デザインの本を読んで基礎を固めてみる。
全く何から手を付けていいかわからない場合は・・・同期、先輩、上司を捕まえて話をしてみるだけでもOKです。
ただ話をしてみるだけでも自分の中のもやもやが整理されるのでほんとおすすめです。（テディベア効果というらしいですね）

#最後に・・・
どうせ新しい挑戦をしたときは失敗なんてするものなのです。
一人で失敗するより周りを巻き込んで失敗していきましょう！(笑):sparkles:
MovableType.netにフォルダー・アーカイブのようなものを作成

MovableType.netにはブログ記事をカテゴリー別や年月日別にリスト化して表示するアーカイブ機能はありますが、ウェブページをフォルダ別のアーカイブページにするフォルダ・アーカイブのようなものはないなと思い、ウェブページのフォルダ別一覧ページを作成してみました。

フォルダ3つとウェブページ5ページをサンプルとして作成しています。フォルダの階層構造とウェブページの配置は下記のようになっています。
![20201207121253_blogpix-thumb-600xauto-7826.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113738/1dd25a7a-f844-9520-6ed9-4a6d589361fc.png)
![20201207121342_blogpix-thumb-600xauto-7827.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113738/f18dd2ca-ab5c-ce1d-3052-acf4933c4b62.png)
ウェブページの一覧ページはウェブページ・アーカイブを使って作成するので、ウェブページ一覧ページ用のウェブページ・アーカイブを新たに追加で下記のように作成しました。

![20201207121441_blogpix-thumb-600xauto-7828.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113738/82b0ac5b-e8eb-3f74-9773-791dbd268a14.png)

URLは「https://ドメイン/フォルダベースネーム.html」としたいので、アーカイブパスを「%C.html」としています。

ウェブページ一覧を表示するテンプレートは下記になります。

```
//属するフォルダの名前を変数 pagefolder に設定
  <$mt:FolderLabel setvar="pagefolder"$> (*1)
  <div class="mainPages__cardWrap">
//変数 pagefolder で表示するウェブページを絞り込み
  <mt:Pages folders="$pagefolder"> (*2)
      <a href="<$mt:PagePermalink$>">
        <$mt:PageTitle encode_html="1"$>
      </a>
  </mt:Pages>
```

書き出すウェブページの一覧は同じフォルダに属するウェブページにfoldersモディファイアを使って絞り込みたいので、フォルダの名前を変数「pagefolder」に格納（テンプレートの*1）して、その値をfoldersモディファイアに代入（テンプレートの*2）することでそれぞれのページで同じフォルダに属するウェブページだけに絞り込んでいます。

これで基本的にはウェブページの一覧ページ用テンプレートの作成は完了です。「https://ドメイン/フォルダベースネーム.html」のURLにアクセスするとフォルダ別のウェブページ一覧が表示されます。

最後にサンプルの「ウェブページ01」のようにフォルダに属さないウェブページがある場合は「https://ドメイン/.html」というURLでもアクセスできるようになってしまうので、アクセスされた時のためにMovableType.netのリダイレクト設定でリダイレクトを設定しておきます。

※こちらは下記の記事を再編集したものです。
[ウェブページの一覧表示 〜フォルダー・アーカイブのようなもの〜](https://www.drivemenuts.com/blog/archives/2020/12/something-like-folder-archives.html "ウェブページの一覧表示 〜フォルダー・アーカイブのようなもの〜")
https://www.drivemenuts.com/blog/archives/2020/12/something-like-folder-archives.html
AWS CloufFormationのスタックをAWS CLIでリスト形式にして出力し、古いものを削除するコマンド

# 基本構文

```console
aws cloudformation describe-stacks --region ap-northeast-1 --profile profile
```

## 変数化

```
REGION=ap-northeast-1 PROFILE=profile
aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE}
```


## 作成順で並び替え

```console
REGION=ap-northeast-1 PROFILE=profile
aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE} | jq -r '.Stacks | sort_by(.CreationTime)'
```

## スタック名と作成日を抜き出す

```
REGION=ap-northeast-1 PROFILE=profile
aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE} | jq -r '.Stacks | .[] | [.StackName, .CreationTime]'
```

## スタック名で特定の文字列が含まれているもの（先頭がooで始まるもの）

下記を入れる

```
select(.StackName | test("^'${PROJECT_HEADER}'")
```

コマンド

```
REGION=ap-northeast-1 PROFILE=profile
PREFIX=my-stack-
aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE} | jq -r '.Stacks | .[] | select(.StackName | test("^'${PREFIX}'")) | .StackName'
```

## スタック名が特定のスタック名以外（masterやdevelop以外のスタックを表示）

```
(contains("develop") | not)
```


```
REGION=ap-northeast-1 PROFILE=profile
aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE} | jq -r '.Stacks | .[] | select(.StackName |(contains("develop") | not) and (contains("master") | not) ) | .StackName'
```

## 特定の件数を表示

下記のようにすると10件目以降を表示する。

```
.['10':][]
```

`sort_by(.CreationTime)` と組み合わせると最新10件を表示する。（10件目以降の古いスタックを表示）

```
REGION=ap-northeast-1 PROFILE=profile
LIMIT=10
aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE} | jq -r '.Stacks | sort_by(.CreationTime) | .['${LIMIT}':][] | .StackName'
```

# 古いスタックをすべて表示

これまでのものを組み合わせて利用。

```
REGION=ap-northeast-1 PROFILE=profile
LIMIT=10
PREFIX=my-stack

aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE} | jq -r '.Stacks | sort_by(.CreationTime) | .['${LIMIT}':][] | { 'StackName': .StackName, 'CreationTime': .CreationTime } | select(.StackName | test("^'${PREFIX}'") and (contains("develop") | not) and (contains("master") | not) ) | .StackName'
```

# 古いスタックを削除する

```bash:delete.sh
stacks=$(aws cloudformation describe-stacks --region ${REGION} --profile ${PROFILE} | jq -r '.Stacks | sort_by(.CreationTime) | .['${LIMIT}':][] | { 'StackName': .StackName, 'CreationTime': .CreationTime } | select(.StackName | test("^'${PREFIX}'") and (contains("develop") | not) and (contains("master") | not) ) | .StackName')


for STACK in ${stacks}
do
  read -n1 -p "${STACK} : delete? (y/N):" yn
  echo ""
  case "$yn" in [yY]*) echo "Deleting ${BUCKET_NAME} ...";
   aws cloudformation delete-stack --stack-name ${STACK} --region ${REGION} --profile ${PROFILE};
   echo "done" ;; *) continue ;;
  esac
done
```
【AWS】デプロイ手順備忘録

#概要
- VPCの作成
- サブネットの作成（パブリックのみ）
- インターネットゲートウェイの設定
- ルートテーブルの設定
- セキュリティーグループの作成
- DBの設置（今回は設置しない）
- EC2インスタンスの作成
- Elastic IPの設定
- デプロイ作業

# 注意
- EC2インスタンス内にDBを構築している場合の設定になります。
- RDBを設置する場合にはそもそもの構成が違うため、プライベートサブネット・エンドポイント・IAMロールの設定などが必要となります。

#VPCの作成
- サイダーは推奨の/16で作成。
- `IAMロールの権限設定`と`CloudWatchの料金アラート設定`を怠らないこと。


#サブネットの作成
- サイダーは推奨の/24で作成。
- AZを1aに設定。

#インターネットゲートウェイの設定
- VPCへのアタッチを忘れずに！

#ルートテーブルの設定
- フルオープン
- サブネットへのアタッチを忘れずに！

#セキュリティグループの作成
- インバウンド：SSH, HTTP, HTTPS
- アウトバウンド：フルオープン

#DBの設置
- RDBは設置ぜず、EC2インスタンス内に設置する。
- RDBは有料かつ`Auroraは非常に高額`なので安易に設置しない。

#EC2インスタンスの作成
- 無料枠内で作成

#Elastic IPの設定
- `アタッチしていない場合には料金が発生`するので注意。


#デプロイ作業
#EC2インスタンスの初期設定
- AWS CLIのダウンロード
- インスタンスへアクセス

```
% mv pemファイルのディレクトリを記述 ~/.ssh
% cd .ssh/
.ssh % chmod 400 ~/.ssh/pemファイル名.pem
.ssh % ssh -i pemファイル名.pem ec2-user@パブリックIPを記述
.ssh % ssh -i ~/.ssh/pemファイル名.pem
```
- 操作用ユーザーの作成（下記AWS公式ページ前段参照）
https://aws.amazon.com/jp/premiumsupport/knowledge-center/new-user-accounts-linux-instance/
- インスタンスの初期設定
- いろいろな方々の資料を参考にしました。必要に応じて取捨選択可能なようです。

```
[ec2-user|~]$ sudo yum install \ git make gcc-c++ patch \openssl-devel \ libyaml-devel libffi-devel libicu-devel \libxml2 libxslt libxml2-devel libxslt-devel \zlib-devel readline-devel \
mysql mysql-server mysql-devel \ImageMagick ImageMagick-devel \epel-release
```

- sshを用いたインスタンスへのアクセス（下記AWS公式ページ後段参照）
https://aws.amazon.com/jp/premiumsupport/knowledge-center/new-user-accounts-linux-instance/
- 以降、必要に応じてミドルウェア等をダウンロードする。
- secret_key_baseの設定

###注意事項
- パブリック IPv4 DNSが表示されていない場合はVPCの設定を修正すること。
- インスタンスに繋がらない場合はセキュリティグループのSSHインバウンドに誤りがないか確認。
- 
####個人的なもの
- データベーステーブルのValueが`latin1`になっていないか特に注意。また、[client]
default-character-set=utf8
[mysql]
default-character-set=utf8の順番で書かないとなぜかDBにログインできないので注意。
- DBはDokerとAWSで記述を変更する必要がある。
忘れずに。

```
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  socket: /tmp/mysql.sock

  # docker用
  # host: db
```
SSHの仕組みについてまとめてみた


この記事は [WESEEK Advent Calendar 2020](https://qiita.com/advent-calendar/2020/weseek) 8日目の記事です。

業務で、初めてSSHにふれる機会があったので、まとめてみることにしました。

## SSHとは
SSHとは **S**ecure **Sh**ell の略で、ネットワークに接続された別のコンピューターをリモートで操作するための方法のことです。自分のPCから遠方にあるサーバーコンピューターを操作する時によく使われます。

## TelnetやFTPとの違い

SSHと似たものに、TelnetやFTP(File Transfer Protocol)があります。ただ、サーバーに送る情報を暗号化せずにそのまま送信するので、セキュリティ的に危険です。
対して、SSHは情報を暗号化し送信するので、セキュリティに強いと言われています。これが、secureと呼ばれる理由です。

## SSHの認証方式
SSHを使って別のコンピューターをリモートで操作するためには、認証を行う必要があります。
認証方法は以下の２つ

1. パスワード認証方式
2. 公開鍵認証方式

### 1. パスワード認証方式
ユーザー名とパスワードを予め決めておき、サーバーに接続する際にこれらを使ってログインする方法です。
以下の特徴があります。
##### メリット
- 馴染み深く、ログインが簡単

##### デメリット
- 管理者が単純なパスワードを設定してしまったり、同じパスワードを使い回ししてしまったりすると、万が一パスワードが知られてしまったときに不正にログインされてしまう危険性があります。

### 2. 公開鍵認証方式
この仕組みを導入することで、秘密鍵を持った人のみがsshログインできるようにします。
検証に合格すれば、「認証成功」と判断し、リモート接続を許可すると言った流れです。

以下の特徴があります。

##### メリット
- パスワード総当り攻撃などで第三者に不正にsshのログインを試みられても、破られる可能性が「パスワード認証方式」よりも低い

##### デメリット
- 鍵を作成したり管理する手間がかかる。


## 秘密鍵 / 公開鍵の確認方法
SSH 鍵が既に生成されているかを確認するために、以下を実行する

```
ls -al ~/.ssh
```
秘密鍵 / 公開鍵はユーザーディレクトリの`./ssh`ディレクトリに保管される。


##　秘密鍵 / 公開鍵の生成方法
以下を実行する
#### 1.秘密鍵と公開鍵の作成コマンドを実行

```
ssh-keygen
```

#### 2.鍵の保存名を設定  
特に指定がなければEnterを押す

```
Enter file in which to save the key (/Users/(ユーザーID)/.ssh/id_rsa): 
```

#### 3.パスフレーズを設定する  
何も入力せずにEnterを押すと、パスフレーズなしの鍵となる

```
Enter passphrase (empty for no passphrase):
# Enter the same passphrase again:
```
鍵の作成完了!


この後、`/Users/(ユーザーID)/.ssh`ディレクトリに移動し、`ls` で確認すると `id_rsa` と `id_rsa.pub` という2つのファイルが作成されているのが確認できます。

- `id_rsa` (秘密鍵)
- `id_rsa.pub` (公開鍵)


## サーバーに公開鍵を置く
※ 今回はmacのターミナルからubuntu 14.04.2に接続しました
### 1.公開鍵認証によるSSH接続
```
$ ssh -i ~/.ssh/id_rsa <ユーザ名>@<IPアドレスもしくはホスト名>
```
### 2.公開鍵の配置
`.ssh`ディレクトリ配下にある `authorized_keys`ファイルにおく


これで、公開鍵をアクセスしたいサーバーに置くことができます。


## まとめ
- SSHとは別のコンピューターにリモートでアクセスする時に使うプロトコル
- TelnetやFTPとは違ってデータが暗号化されるのでセキュリティ的により安全
- SSHの二つの方式のうち「パスワード認証方式」よりも安全な「公開鍵認証方式」を使うようにしましょう!



※この記事は [WESEEK Tips wiki](https://tips.weseek.co.jp/) に 2020/12/ に投稿された記事の転載です。
Tips wiki では、IT企業の技術的な情報やプロジェクトの情報を公開可能な範囲で公開してます。

## 参考
- [SSHとは |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word11722.html)
- [お前らのSSH Keysの作り方は間違っている
](https://qiita.com/suthio/items/2760e4cff0e185fe2db9)
- [「よく分かる公開鍵認証」～初心者でもよくわかる！VPSによるWebサーバー運用講座(2)](https://knowledge.sakura.ad.jp/3543/)
- [SSH公開鍵認証で接続するまで](https://qiita.com/kazokmr/items/754169cfa996b24fcbf5)
QRを作ってストレージに置く。

#やりたいこと
ある文字列のQRコードを作って、ストレージ上に配置する。

#定義と実際に置く


```javascript
const QRCode = require('qrcode');
const {Storage} = require('@google-cloud/storage');

const bucketName = 'bucket_name';
const storage = new Storage();
const bucket = storage.bucket(bucketName);


var filename ='shashin.png';
var filepath = path.join(os.tmpdir(), filename);
console.log(filepath);

await QRCode.toFile(filepath, 'QRにしたい文字列');

await storage
    .bucket(bucketName)
    .upload(filepath, {gzip: false})
    .then(res => {
    console.log(res[0].metadata);
    console.log(`${filename} uploaded to ${bucketName}.`);
    })
    .catch(err => {
    console.error('ERROR:', err);
    });
パケットキャプチャ 3 dumpcap リファレンス

https://www.wireshark.org/docs/man-pages/dumpcap.html

> SYNOPSIS
> dumpcap 
> [ -a|--autostop <capture autostop condition> ] ... 
> [ -b|--ring-buffer <capture ring buffer option>] ... 
> [ -B|--buffer-size <capture buffer size> ] 
> [ -c <capture packet count> ] 
> [ -C <byte limit> ] 
> [ -d ] 
> [ -D|--list-interfaces ] 
> [ -f <capture filter> ] 
> [ -g ] 
> [ -h|--help ] 
> [ -i|--interface <capture interface>|rpcap://<host>:<port>/<capture interface>|TCP@<host>:<port>|- ] 
> [ -I|--monitor-mode ] 
> [ -k <freq>,[<type>],[<center_freq1>],[<center_freq2>] 
> [ -L|--list-data-link-types ] 
> [ -M ] 
> [ -n ] 
> [ -N <packet limit> ] 
> [ -p|--no-promiscuous-mode ] 
> [ -P ] 
> [ -q ] 
> [ -s|--snapshot-length <capture snaplen> ] 
> [ -S ] 
> [ -t ] 
> [ -v|--version ] 
> [ -w <outfile> ] 
> [ -y|--linktype <capture link type> ] 
> [ --capture-comment <comment> ] 
> [ --list-time-stamp-types ] 
> [ --time-stamp-type <type> ]

....

> DESCRIPTION
> Dumpcap is a network traffic dump tool. 
> It lets you capture packet data from a live network and write the packets to a file. 
> Dumpcap's default capture file format is pcapng format. 
> When the -P option is specified, the output file is written in the pcap format.
> 
> Without any options set it will use the libpcap, Npcap, or WinPcap library 
> to capture traffic from the first available network interface 
> and writes the received raw packet data,
> along with the packets' time stamps into a pcap file.
> 
> If the -w option is not specified, 
> Dumpcap writes to a newly created pcap file with a randomly chosen name.
> If the -w option is specified, 
> Dumpcap writes to the file specified by that option.
> 
> Packet capturing is performed with the pcap library. 
> The capture filter syntax follows the rules of the pcap library.

....

> Dumpcapは、ネットワークトラフィックダンプツールです。 ライブネットワークからパケットデータをキャプチャし、パケットをファイルに書き込むことができます。 Dumpcapのデフォルトのキャプチャファイル形式はpcapng形式です。 -Pオプションを指定すると、出力ファイルはpcap形式で書き込まれます。
> 
> オプションを設定しない場合、libpcap、Npcap、またはWinPcapライブラリを使用して、最初に使用可能なネットワークインターフェイスからトラフィックをキャプチャし、受信した生のパケットデータとパケットのタイムスタンプをpcapファイルに書き込みます。
> 
> -wオプションが指定されていない場合、Dumpcapはランダムに選択された名前で新しく作成されたpcapファイルに書き込みます。 -wオプションが指定されている場合、Dumpcapはそのオプションで指定されたファイルに書き込みます。
> 
> パケットキャプチャは、pcapライブラリを使用して実行されます。 キャプチャフィルターの構文は、pcapライブラリの規則に従います。
>
ReactNative CLIをMac(10.15.7)にインストールする

頻繁に設定するものでは無いけど、貸与端末とかのセットアップ時のためにメモ。
ネイティブのコードを書くことがあるので、Expoではなく、ReactNative CLIの方をインストールする。
内容はほとんど[公式サイト](https://reactnative.dev/docs/environment-setup)から引用

## 前提条件
- MacのバージョンはCatalina(10.15.7)
- Macの管理者権限が付与されているアカウントで実施
- [HomeBrew](https://brew.sh/index_ja)はインストール済みである


## iOS、Android共通点設定
#### 1.Nodeのインストール
(nodeのバージョンを指定したい場合はnodebrewを入れる)

```
brew install node
```

#### 2.Watchmanのインストール
```
brew install watchman
```
  
## iOS設定
#### 1. Xcodeのインストール
[XCode](https://apps.apple.com/us/app/xcode/id497799835?mt=12)をインストールする
(Xcodeのバージョンに指定がある場合は[Apple Developer](https://idmsa.apple.com/IDMSWebAuth/signin?appIdKey=891bd3417a7776362562d2197f89480a8547b108fd934911bcbea0110d07f757&path=%2Fdownload%2F&rv=1)からxipファイルをダウンロードする)　※要Developerアカウント

#### 2. コマンドラインツールのインストール
「Xcode」を開き、メニューの「Xcode」-「Preferences...」-「Locations」を選択し、「Command Line Tools」がXcodeのバージョンと同じものをインストールする

#### 3. シミュレーターのインストール
「Xcode」を開き、メニューの「Xcode」-「Preferences...」-「Components」を選択し、使用するシミュレーターをインストールする

#### 4. CocoaPodsのインストール
```
sudo gem install cocoapods
```

## Android設定

#### 1. JDKのインストール
RNの公式サイトにはJDK8以降なら大丈夫と記載されているが、14だとAndroidビルド時にエラーになるらしい。 (公式サイトのインストール手順は8になっている) 

```
brew cask install adoptopenjdk/openjdk/adoptopenjdk8
```

#### 2. Android Studioのインストール
[Android Studio](https://developer.android.com/studio)をダウンロードして、インストールする

インストール時に以下をチェックボックスの以下を選択する

- Android SDK
- Android SDK Platform
- Android Virtual Device
- Performance (Intel ® HAXM)　(Hyper-V未使用の場合)

#### 3. Android SDKのインストール
ReacrNativeでネイティブコードを書くには「Android 10 (Q)」が必要らしい  
Andoroid Studioを開き、メニューの「Andoroid Studio」-「Preferences...」-「Appearance & Behavior」-「System Settings」-「Android SDK」を選択する。
「SDK Platforms」タブから`Android 10 (Q)`を選択し、`右下のチェックボックス「Show Package Details」にチェック`を入れパッケージの中身を表示する。

以下にチェックが入っていることを確認する (デフォルトで入ってないものが多いので注意)

- Android SDK Platform 29
- Intel x86 Atom_64 System Image
- Google APIs Intel x86 Atom_64 System Image
- Google Play Intel x86 Atom System Image　(エミュレータで使用するっぽい)

「SDK Tools」タブの右下のチェックボックス「Show Package Details」にチェックを入れパッケージの中身を表示し、`「AndroidSDKBuild-Tools」内の「29.0.2」にチェック`が入っていることを確認する  


#### 4. エミュレータのインストール
Android Studioを開き、「AVD Manager」から「+ Create Virtual Device...」ボタンを押下し、好きな端末を選択して「Next」を押下し、「System Image」でQ(API Level 29)を選択する  

#### 5. 環境変数(ANDROID_HOME)の設定
`~/.zshrc`に以下を記述する  

``` script
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
```

## 動作確認

#### 1. コマンドでプロジェクトを作成  
(TypeScriptで作成する場合、コマンドの後ろに「--template react-native-template-typescript」オプションを指定)

```
npx react-native init AwesomeProject
```

#### 2. プロジェクトフォルダ(AwesomeProject)内でメトロを実行
```
cd AwesomeProject
npx react-native start
```

#### 3. iOSを起動
メトロを実行したターミナルとは別のターミナルから、プロジェクトフォルダ内で以下を実行

```
npx react-native run-ios
```

#### 4. Androidを起動
プロジェクトフォルダ内で以下を実行

```
npx react-native run-android
```
<br>
シミュレーター(エミュレーター)でアプリが起動出来れば設定完了
WireSharkでパケットキャプチャ 2

タスクスケジューラでのCLIでの自動実行を検証する

dumpcap.exeだけ動けばいいのでWireSharkをインストールしたコンピュータからインストールフォルダごとコピーしてきてdumpcap.exeを動かしてみる

```
C:\app\Wireshark>dumpcap.exe
```

VC140.dllがないエラー表示
VSCodeの再配布パッケージをインストールし再実行

```
C:\app\Wireshark>dumpcap.exe
dumpcap: Unable to load Npcap or WinPcap (wpcap.dll); you will not be able to
capture packets.

In order to capture packets Npcap or WinPcap must be installed. See

        https://nmap.org/npcap/

for a downloadable version of Npcap and for instructions on how to
install it.
```

やはりnpcapのインストールは必要か..
本番環境にいちいち入れんの負荷高いねん..
Josh Skeen, David Greenhalgh著　『Kotlinプログラミング』　章末問題に挑戦(第３章)

　Josh Skeen, David Greenhalgh著『Kotlinプログラミング』には章末問題がある。残念なことに解答がないので、[第２章](https://qiita.com/guijiu/items/dd3313f2a73401e29bc4)に引き続き本を読み進めながら答案を記述する。
　もし回答に間違いや意見があれば、ぜひご指摘いただきたい。

#第３章　章末問題 回答案

##　3.12 チャレンジ！　オーラを補強する
```kotlin:Game.kt
fun main() {
    val name = "ミゲール"
    var healthPoints = 89
    val isBlessed = true

    val karma = (Math.pow(Math.random(), (110 - healthPoints) / 100.0) * 20).toInt()
//    println("karma = $karma")     // karma値を表示

    // Aura
    val auraColor = when (karma) {
        //　karma値に応じてauraColorを指定
        in 0..5 -> "赤"
        in 6..10 -> "橙"
        in 11..15 -> "紫"
        in 16..20 -> "緑"
        else -> "NONE"
    }

    val healthStatus = when (healthPoints) {
        100 -> "は、最高だぜ!"
        in 90..99 -> "は、かすり傷を負った。"
        in 75..89 -> if (isBlessed) {
            "は、ちょっと傷があるが、このくらいすぐに治るぜ!"
        } else {
            "は、ちょっと傷がある。"
        }
        in 15..74 -> "は、かなりヤラレテいるようだ"
        else -> "は、ひどい状態だ!"
    }

    // Player status
    println(
        "(後光: $auraColor) " +
                "(福者: ${if (isBlessed) "はい" else "いいえ"})"
    )
    println("$name$healthStatus")
}
```

##3.13 チャレンジ！　変更可能な状態フォーマット
　p46の欄外の訳注に「たぶん文字列テンプレートと内挿を使うことになるでしょう」との記述があるが、私自身、以下の回答で「文字列テンプレート」は使ったが、「内挿」をどのように使うのか分からなかった（p43に「内挿」の説明があるが、どの式を評価すればいいのか分からない）。どのような式を評価すればいいのかわかる方がいれば、ご意見をお聞かせ願いたい。

```kotlin:Game.kt
fun main() {
    val name = "ミゲール"
    var healthPoints = 100
    val isBlessed = true

    val karma = (Math.pow(Math.random(), (110 - healthPoints) / 100.0) * 20).toInt()
//    println("karma = $karma")     // karma値を表示

    // Aura
    val auraColor = when (karma) {
        //　karma値に応じてauraColorを指定
        in 0..5 -> "赤"
        in 6..10 -> "橙"
        in 11..15 -> "紫"
        in 16..20 -> "緑"
        else -> "NONE"
    }

    val healthStatus = when (healthPoints) {
        100 -> "は、最高だぜ!"
        in 90..99 -> "は、かすり傷を負った。"
        in 75..89 -> if (isBlessed) {
            "は、ちょっと傷があるが、このくらいすぐに治るぜ!"
        } else {
            "は、ちょっと傷がある。"
        }
        in 15..74 -> "は、かなりヤラレテいるようだ"
        else -> "は、ひどい状態だ!"
    }

    // Player status
    var h = "$name$healthStatus"
    val statusFormatString = "(HP: $healthPoints)(Aura:$auraColor) -> $h"
    println(statusFormatString)

//    println(
//        "(後光: $auraColor) " +
//                "(福者: ${if (isBlessed) "はい" else "いいえ"})"
//    )
}

```

（2020/12/08現在）


[第２章チャレンジ！答案](https://qiita.com/guijiu/items/dd3313f2a73401e29bc4)
[第３章チャレンジ！答案](https://qiita.com/guijiu/items/672b8daf014d4e6bb346)
[第４章チャレンジ！答案](https://qiita.com/guijiu/items/08dfa397b989b7ecdb3a)
[第７章チャレンジ！答案](https://qiita.com/guijiu/items/452cfa51bee6c4a4af9d)
[第８章チャレンジ！答案](https://qiita.com/guijiu/items/9a7871b1e41bdabd85e7)
GitLab CEで使えるようになったパッケージレジストリへ、Mavenパッケージを登録してみる

毎度、ググっても出てこない小ネタを取り扱っております。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。

# 0. はじめに

今年の5月に、これまでGitLabで有料版だった機能を無料版(CE:Community Edition)でも使えるようにするよ、というアナウンスがされました。

18 GitLab features are moving to open source | GitLab
https://about.gitlab.com/blog/2020/03/30/new-features-to-core/

その中で、各種パッケージを入れるリポジトリーも無料になるよということでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/e2d28e9a-0003-3a98-aa55-803dcebec593.png)

Maven (Java) repositoryは、社内でも取り扱いが多いので手ぐすね引いて待っておりました。
それの検証記事になります。

# 1. ドキュメント

基本的に以下のページに書いてあります。
Maven packages in the Package Repository | GitLab
https://docs.gitlab.com/ee/user/packages/maven_repository/index.html#create-maven-packages-with-gitlab-cicd

こちらの日本語サイトもそのうち翻訳されるのではないでしょうか。
GitLab Maven Repository | GitLab
https://gitlab-docs.creationline.com/ee/user/packages/maven_repository/

今回やってみたかったのは、CI/CDでの操作なので、以下を参照しました。

Maven packages in the Package Repository | GitLab
https://docs.gitlab.com/ee/user/packages/maven_repository/index.html#create-maven-packages-with-gitlab-cicd

# 2. 必要なファイル

以下の3つのファイルの修正が必要です。

1. .gitlab-ci.ymlファイルの修正
1. MavenのCI用設定ファイル(ci_settings.xml)
1. pom.xmlファイルの修正

それぞれ中身を見てみます。

## 2-1. .gitlab-ci.ymlファイルの修正

mvn deployコマンドを追加して、`ci_settings.xml`を参照するように変更します。
imageは、お好きに変更してください。

```yaml:.gitlab-ci.yml
deploy:
  image: maven:3.6-jdk-11
  script:
    - 'mvn deploy -s ci_settings.xml'
  only:
    - master
```

## 2-2. MavenのCI用設定ファイル(ci_settings.xml)

ci_settings.xmlを以下の内容で作成します。

```xml:ci_settings.xml
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
  <servers>
    <server>
      <id>gitlab-maven</id>
      <configuration>
        <httpHeaders>
          <property>
            <name>Job-Token</name>
            <value>${env.CI_JOB_TOKEN}</value>
          </property>
        </httpHeaders>
      </configuration>
    </server>
  </servers>
</settings>
```

## 2-3. pom.xmlファイルの修正

pom.xmlに以下の項目を追記します。

```xml:pom.xml
<repositories>
  <repository>
    <id>gitlab-maven</id>
    <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
  </repository>
</repositories>
<distributionManagement>
  <repository>
    <id>gitlab-maven</id>
    <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
  </repository>
  <snapshotRepository>
    <id>gitlab-maven</id>
    <url>${env.CI_SERVER_URL}/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
  </snapshotRepository>
</distributionManagement>
```

# 3. それぞれのファイルの相関関係図

図解してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/edcaba51-6ca6-6592-fc80-7431cb7c6667.png)


# 4. 実行確認

こんな感じに入るはず。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/91795426-4178-a5c3-5fdb-b4db352151cb.png)

# 5. まとめ

GitLab CI/CDでだけ影響のある部分を、ci_settings.xmlで書き出すのがポイントでしょうか。
Artifactsで保存するのも良いですが、期限が切れたら削除されてしまったり、場所が分かりにくかったりするので、レジストリーに保存するのはお勧めです。