OmniAICreator/Qiita-1.07M huggingface dataset:
【SwiftUI】特定の条件下でTabを非表示にする

# はじめに
`SwiftUI-Introspect`を使ってSwiftUIではできないことをサポートしてもらいます。

https://github.com/siteline/SwiftUI-Introspect

# ライブラリのインストール
全体的な流れはこちらを参考にしてください

https://tech.amefure.com/swift-package-manager

:::note warn
**注意**
`SwiftUIIntrospect`のみチェックをいれます
![スクリーンショット 2023-08-01 16.18.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/31800ab3-7bad-0ab0-78fc-fb8934daa2a3.png)
:::

# サンプルアプリ
![Simulator Screen Recording - iPhone 14 Pro - 2023-08-01 at 16.20.46.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/1c337202-e75d-efa5-4dc8-ec18bacd6b25.gif)

# 実装
```swift
import SwiftUI
import SwiftUIIntrospect

struct ContentView: View {
    @State private var isHidden = false
    
    var body: some View {
        TabView {
            Button {
                isHidden.toggle()
            } label: {
                Text("Tab1")
            }
            .tabItem {
                Label("1", systemImage: "1.circle.fill")
            }
            
            Button {
                isHidden.toggle()
            } label: {
                Text("Tab2")
            }
            .tabItem {
                Label("2", systemImage: "2.circle.fill")
            }
            
            Button {
                isHidden.toggle()
            } label: {
                Text("Tab3")
            }
            .tabItem {
                Label("3", systemImage: "3.circle.fill")
            }
        }
        .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { tabView in
            tabView.tabBar.isHidden = isHidden
        }
    }
}
```

# おわり
SwiftUIを使う時に`SwiftUI-Introspect`は必須ですね
Slack で MarkDown のテキストを WYSIWYG に変換する ショートカットは Shift + Command + F (Macの場合) (書式設定)

- コードブロックを変換してくれる
- 箇条書きは変換してくれない
- 見出しは太字にしてくれるが元のシャープ ( # ) が残ってしまう

という挙動みたいだ

# 整形前

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89618/649bd128-e5b9-827a-1349-4da08c4e583d.png)

# 整形後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89618/cd8ed926-2e86-81c1-ec4b-f70db2e631fc.png)


# チャットメンバー募集


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

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ
【備忘録】～Linuxのtreeコマンド～インストールから使い方まで

## はじめに
Windows10のWSL環境でUbuntuを使っていて、最近treeコマンドをインストールしたので、その手順を備忘録としてここに残そうと思います。

## 参考記事

https://zenn.dev/yusuke_docha/articles/2eaba793fb2b0d

## treeとは
treeコマンドを使うことによってディレクトリ構造を確認できたり、markdown記法などで以下のような表現を行うことができるようになります。

```
test
├── memo
│   ├── メモ1.txt
│   ├── メモ2.txt
│   ├── メモ3.txt
│   └── メモ4.txt
├── メモ1.txt
└── メモ2.txt
```

## treeが存在するか確認
```
$which tree
# 存在する場合は以下のパスが表示されるが、存在しない場合は何も表示されない
/usr/bin/tree
```

## treeをインストール
```
sudo apt install tree
```

## treeのアンインストール
```
sudo apt remove tree
```

## treeコマンドの基本的な使い方
以下のサイトが網羅していた感じ
=> [【超便利】treeコマンドのインストール方法から使い方まで徹底解説](https://itc.tokyo/linux/tree-command/)

#### 使いそうなものを一部抜粋
- 基本的には以下のような使い方
```
tree ディレクトリ オプション
```

- カレントディレクトリからのツリーを表示
```
# カレントディレクトリからのツリーを表示
tree
もしくは
tree .
```

- ディレクトリを指定して表示
```
# ディレクトリを指定して表示
tree ディレクトリ名
```

- 指定した階層までの表示を制限
```
# 指定した階層までの表示を制限
tree -L 2
```

- 隠しファイルも表示
```
# 隠しファイルも表示
tree -a
```

- ディレクトリのみを表示
```
# ディレクトリのみを表示
tree -d
```

## markdownへの適用方法
markdownへの適用方法は、treeコマンドでターミナルに出力されたディレクトリ構造をmarkdownのコードブロックにコピペするだけで良いので意外と簡単です。
```
$tree test -L 2
test
├── memo
│   ├── メモ1.txt
│   ├── メモ2.txt
│   ├── メモ3.txt
│   └── メモ4.txt
├── メモ1.txt
└── メモ2.txt
```

## さいごに
GitHubのファイル構造の表現のために使いたくなってインストールしてみました。
長々と最後までご覧いただきありがとうございました。
さくっとVue3インストール

さくっとVue3をインストールして、デモページを表示することを目標にします。


# 準備するもの
- MacOS
- Nodeの最新バージョン
- VSCode

この３つ！

## Mac
MacBookなど

## Nodeの最新バージョン
※すでにNodeが使える場合は飛ばしてください。

最新のNode.jsをインストールするために、Nodeバージョン管理ツールを入れます。
ツールは **volta** を使用します。


ターミナルを開く
Macの右上の虫眼鏡マークをクリックして、「ターミナル」と入力してターミナルを開きます

voltaインストール
```
# install Volta
curl https://get.volta.sh | bash
```
最新のNodeをインストール
```
# install Node
volta install node

# Nodeバージョン確認
node -v
```
これで最新のNodeが使えるようになります。


# Vue3インストール
続いて、Vueのテンプレートをインストールします
自分の作業ディレクトリ`$HOME/projects/sample`等を用意しておくと、今後ちらからずに済みます！


```
# 作業ディレクトリに移動
cd $HOME/projects/sample

# 最新のVue3インストール
npm init vue@latest
```

オプション選択
```
✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add an End-to-End Testing Solution? … No / Cypress / Playwright
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes

Scaffolding project in ./<your-project-name>...
Done.
```
- Project name -> vue3-sample
- TypeScript-> Yes
- JSX Support -> No
- Vue Router -> Yes
- Pinia -> Yes
- Vitest -> Yes
- an End-to-End -> No
- ESLint -> Yes
- Prettier -> Yes


上から順番に選択していきます。最低限必要なオプションをYesとします。

# Vue3を動かす
ここからはVSCode上で作業します。

上記でプロジェクトが作成されたので、vue3-sampleをVSCodeで開きます。
上部のターミナルタブをクリックして、「新しいターミナル」を選択します。

ターミナルが開かれたら、起動させます
```
# Node.js 関連のパッケージをインストール
npm install

# 開発サーバーを起動
npm run dev
```

Webブラウザ(Chromeなど)でlocalhost:5173にアクセスして、デモページが表示されたら完了です。

お疲れさまでした！
EthereumのActを使う

この文書は株式会社 [proof ninja](https://proof-ninja.co.jp/) の助成を受けたものです．

EthereumのEVMプログラムの仕様記述言語であるActを使ってみる．記述した仕様はSMTソルバに自動証明させたり，Coqコードに変換してCoqの中で自分で証明したりできるらしい．
Actの仕様とEVMでの実装の同値性はHevmというエンジンで自動証明するらしい．

# インストール

ソースコードはここ．
https://github.com/ethereum/act

ビルドにはNixというパッケージマネージャを使うので，まず[Nixをインストール](https://nixos.org/download.html)．

ActのReadmeには nix build でビルドすると書いてあるが，自分の環境では
```
$ nix build --extra-experimental-features nix-command --extra-experimental-features flakes
```
とする必要があった．

`act/result/bin/` にバイナリが生成される．パスを通すなどしよう．

また，Coqコードを生成して使いたい場合にはActLibをインストールする必要がありそうなので，`act/lib/ActLib.v` もビルドする．`act/lib/`の下で
```
$ coqc -R . ActLib ActLib.v
```
としてビルドした．自分はOpamでインストールしたCoqを使っているので，`~/.opam/バージョン/lib/coq/user-contrib/` の下に `ActLib` というディレクトリを作り，
```
cp ActLib.* ~/.opam/バージョン/lib/coq/user-contrib/ActLib
```
とした．

# 使ってみる
[ActBook](https://ethereum.github.io/act/introduction.html)に書いてあることをやってみた．ここではCoqの部分のみを見ていく．

以下はActBookのサンプルActコードに自分で理解したなりのコメントを加えたもの．
非負整数 b, e に対して b の e 乗を計算する状態機械を定義しようとしている．

```
// Exponent というコントラクトを宣言する．
constructor of Exponent
// 二つの符号なし整数 _b, _e を引数に取ることにする．
interface constructor(uint _b, uint _e)

iff // コントラクトが生成されるための条件．ここでは _e が正という条件を課す．

    _e > 0

// Exponentの状態変数を定義する．
// ここでは b, e, r の3個で，初期値として _b, _e, _b を与える．
creates

    uint b := _b
    uint e := _e
    uint r := _b

// Exponentの状態遷移を定義する．
behaviour exp of Exponent
interface exp()

// 状態遷移が起こるための条件を書く．
// ここでは e > 1 であることと， r*b, e-1 が uintの範囲に収まっていることとする．
iff

    e > 1

iff in range uint

    r * b
    e - 1

// 状態遷移を定義する．ここでは，上の条件のもとで，(r, e, b) => (r*b, e-1, b) と定める．
storage

    r => r * b
    e => e - 1
    b

```
上のコードを `Exponent.act` として保存し，
```
act coq --file Exponent.act > Exponent.v
```
で以下のようなCoqコード `Exponent.v` が生成される．

```coq
(* --- GENERATED BY ACT --- *)

Require Import Coq.ZArith.ZArith.
Require Import ActLib.ActLib.
Require Coq.Strings.String.

Module Str := Coq.Strings.String.
Open Scope Z_scope.

Record State : Set := state
{ b : Z
; e : Z
; r : Z
}.

Definition exp0 (STATE : State)  :=
state (b STATE) (((e STATE) - 1)) (((r STATE) * (b STATE))).

Definition Exponent0 (_b : Z) (_e : Z) :=
state (_b) (_e) (_b).

Inductive reachable  : State -> State -> Prop :=
| Exponent0_base : forall (_b : Z) (_e : Z),
     (_e > 0)
  -> ((0 <= _b) /\ (_b <= (UINT_MAX 256)))
  -> ((0 <= _e) /\ (_e <= (UINT_MAX 256)))
  -> reachable (Exponent0 _b _e) (Exponent0 _b _e)

| exp0_step : forall (BASE STATE : State),
     reachable BASE STATE
  -> ((e STATE) > 1)
  -> ((0 <= ((r STATE) * (b STATE))) /\ (((r STATE) * (b STATE)) <= (UINT_MAX 256)))
  -> ((0 <= ((e STATE) - 1)) /\ (((e STATE) - 1) <= (UINT_MAX 256)))
  -> ((0 <= (r STATE)) /\ ((r STATE) <= (UINT_MAX 256)))
  -> ((0 <= (e STATE)) /\ ((e STATE) <= (UINT_MAX 256)))
  -> ((0 <= (b STATE)) /\ ((b STATE) <= (UINT_MAX 256)))
  -> reachable BASE (exp0 STATE )
.
```
状態 `s` から 状態 `s'` への状態遷移が可能であることを表す `reachable s s'` が帰納的述語として定義された．
ActBookでは，この状態機械が実際に累乗を計算していること
```coq
Theorem exp_correct : forall base s,
  reachable base s -> e s = 1 -> r s = (b base) ^ (e base).
```
を証明している．これは普通のCoqの証明なのでここでは触れない．

他にはCoqで証明している例が見つからなかったので，何か試したいところ．
ruby.wasmの初期化シーケンス

[ruby.wasm](https://github.com/ruby/ruby.wasm)をブラウザで動かすためには次のJavaScriptを使って`ruby.wasm`を初期化します。

```html
<script src="https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@2.1.0/dist/browser.script.iife.js"></script>
```

上記のJavaScriptのソースコードは
https://github.com/ruby/ruby.wasm/blob/main/packages/npm-packages/ruby-wasm-wasi/src/browser.script.ts です。

## 初期化シーケンス

`browser.script.ts`で行っている初期化処理をシーケンス図にします。

```mermaid
sequenceDiagram
    Note over browser.script.ts: CRubyのWebAssemblyバイナリをダウンロード<br/>バイナリをコンパイルして<br/>WebAssembly moduleを作成
    browser.script.ts->>DefaultRubyVM: WebAssebly module
    Note over DefaultRubyVM,WASI: WASIを初期化<br/>このWASIはwasmerの実装
    DefaultRubyVM->>WASI: インスタンス生成
    DefaultRubyVM->>RubyVM: インスタンス生成
    Note over DefaultRubyVM,Printer: WebAssembly moduleから<br/>JavaScriptの世界の関数を呼び出せるように<br/>コールバック関数を登録する<br/>ここでコールバックを設定するインタフェースの仕様がWASI
    rect rgb(191, 223, 255)
    DefaultRubyVM->WASI: importsを取得
    DefaultRubyVM->>RubyVM: imports
    Note over RubyVM: importsを使って<br/>WebAssembly moduleに<br/>コールバック関数を登録する
    DefaultRubyVM->>Printer: インスタンス生成
    DefaultRubyVM->>Printer: WebAssebly module の imports
    Note over Printer: 標準出力への書き込みフック<br/>console.logへ書き込む
    end
    Note over DefaultRubyVM,Printer: RubyVMと関連オブジェクトを初期化
    rect rgb(191, 223, 255)
    Note over DefaultRubyVM: WebAssembly moduleから<br/>WebAssembly インスタンス生成
    DefaultRubyVM->>WASI: WebAssemblyインスタンス
    DefaultRubyVM->>RubyVM: WebAssemblyインスタンス
    DefaultRubyVM->>Printer: WebAssemblyと標準出力を共有するためのメモリ割当
    DefaultRubyVM->>RubyVM: initialize
    end
    DefaultRubyVM-->>browser.script.ts: RubyVM
    browser.script.ts->>RubyVM: eval(実行したいRubyスクリプト)
```


`DefaultRubyVM`は`RubyVM`を初期化します。
上記のシーケンス図では複雑な処理を行っていますが、ひとたび初期化が完了すれば`RubyVM`オブジェクトがあれば十分です。

`RubyVM`とWebAssemblyの相互呼び出しはWASI仕様に則っています。
インスタンス化したりお互いの呼び出し関数を登録し合ったりと面倒な手続きがあります。その手続きを`DefaultRubyVM`が行っています。
上記シーケンス図の青色の部分です。

## 初期化完了後

初期化完了後`browser.script.ts`は`RubyVM`オブジェクトの`eval`メソッドを呼び出してRubyスクリプトを実行します。`RubyVM`は「WebAssembly化されたCRuby」とJavaScriptの世界を繋ぐためのラッパーとなるJavaScriptのクラスです。
JavaScriptの世界から「WebAssembly化されたCRuby」を呼び出すときは`RubyVM`のメソッドを呼び出します。
たとえば、JavaScriptから`RubyVM`の`eval`メソッドを呼び出すと、任意の文字列をRubyスクリプトとして実行出来ます。

```js
vm.eval('p "Hello, World!"');
```

反対に「WebAssembly化されたCRuby」からJavaScriptの世界の何かを呼び出すときは`RubyVM`のメソッドを呼び出します。
たとえば、「WebAssembly化されたCRuby」からの標準出力への書き込むと、`RubyVM`でフックして`console.log`の呼び出しに置き換えます。
その結果、上記のJavaScriptの実行結果はコンソールへの`Hello, World!`の出力になります。

シーケンス図にすると次のようになります。

```mermaid
sequenceDiagram
    JavaScritp->>RubyVM: eval('p "Hello, World!"')
    RubyVM->>WebAssembly化されたCRuby: rbEvalStringProtect('p "Hello, World!"')
    Note over WebAssembly化されたCRuby: 'p "Hello, World!"'を<br/>Rubyとして評価して実行
    WebAssembly化されたCRuby->>RubyVM: 標準出力に"Hello, World!"
    RubyVM->>JavaScritp: console.log("Hello, World!")
```


ここで「WebAssembly化されたCRuby」と呼んでいるのは、CRubyをコンパイルしたWebAssemblyバイナリを、ブラウザ上で実行したWebAssemblyインスタンスのことです。
devise導入後ログアウトできないエラーの解決

deviseを導入してログアウトの時にcreated at に関するエラー文が出ていた。
migration fileに

```ruby
       ## Recoverable
       t.string   :reset_password_token
       t.datetime :reset_password_sent_at
 
       ## Rememberable
       t.datetime :remember_created_at
```

これを記述したら解決した。
djangoで、事前にログイン・ユーザー認証をしていないと使用できないRESTAPIを作成する。

# 結論
`djangorestframework`を使用して、セッション認証を通すように実装します。そうすることで、事前にブラウザ上でログインをしていないと使用できないRESTAPIを作成できます。

# 前提
- djangoでWebアプリを既に作成している。何かしらの方法でログイン・ユーザー認証を実装できている状態であるとします。
- 使用モジュールの各バージョンは以下です
  - Django 3.2.20

# 作成手順
1.  pipで下記のモジュールをインストールします。
    ```
    pip install djangorestframework
    pip install markdown       
    pip install django-filter
    ```
1. `settings.py`に下記のコードを追加します。
    ```settings.py
    # ...
    INSTALLED_APPS = [
        # ...
        'rest_framework', # 追加
        # ...
    ]
    # ...
    # 追加
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.SessionAuthentication',
        ]
    }
    ```
1. 下記に則ったコードでRESTAPIの処理となるViewを作成します。
    ```views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.permissions import IsAuthenticated

    class AuthTestView(APIView):

        permission_classes = [IsAuthenticated]

        def get(self, request, format=None):
            return Response({"message": "OK"})
    ```
1. urlsに作成したViewをルーティングします。

これで、ルーティングしたURLにアクセスすると、ログイン・ユーザー認証できていないと使用できないことが確認できます。
- ログイン・ユーザー認証済み
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3071987/7a81f809-cdc9-c15c-e53f-2c8ef0ebd66e.png)
- 未ログイン・未ユーザー認証
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3071987/eae1af0a-0911-fca3-e628-0111e6441bca.png)

# 補足
- `settings.py`で、`DEFAULT_AUTHENTICATION_CLASSES`を設定しました。これはプロジェクトを通してRESTAPIはセッション認証を通すという設定です。もし各Viewで設定したい場合は下記のように`authentication_classes`をView上で設定すればOKです。
    
```views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication # 追加
from rest_framework.permissions import IsAuthenticated

class AuthTestView(APIView):
    
    authentication_classes = [SessionAuthentication] # 追加
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        return Response({"message": "OK"})
```

- Viewを作成するときに、今回は`APIView`を継承しています。これは他にも継承できるものがあり、それぞれの機能によって役立つものがあるようです。詳しくは下記の記事が参考になります。

https://qiita.com/huma17ax/items/f5a8302b055eca8e0e77

- POSTメソッドを作成する場合、設定によってはクライアント側がcsrftokenを持たせて通信しないと認証失敗となります。この場合はクライアント側にcsrftokenを持たせて通信をするようにしてください。

以上です。

# 参考

https://www.django-rest-framework.org/#installation

https://qiita.com/sasanoworkz/items/87cf15f0ab6c936b31a5

 https://qiita.com/huma17ax/items/f5a8302b055eca8e0e77
Ebitenを使ってボールをランダムに動かすゲームを作成する

EbitenというGo言語で開発された2Dゲームエンジンを使って、ボールがランダムに動き回るシンプルなゲームを作成する方法を記載します。

```go
package main

import (
	"image/color" // 色を操作するためのパッケージ
	"log"         // ログ出力を扱うためのパッケージ
	"math/rand"   // 乱数を生成するためのパッケージ
	"time"        // 時間に関する操作を扱うためのパッケージ

	"github.com/hajimehoshi/ebiten/v2"            // Ebitenのゲームエンジンのパッケージ
	"github.com/hajimehoshi/ebiten/v2/ebitenutil" // Ebitenのユーティリティパッケージ
)
```

次に、ゲーム画面のサイズとボールの大きさを定数で定義します。

```go
// 画面のサイズとボールの大きさを定義
const (
	screenWidth  = 640
	screenHeight = 480
	ballSize     = 10
)
```

そして、ゲームの状態を表す構造体を定義します。ここではボールの位置(x, y)と速度(vx, vy)を保持します。

```go
// Game はゲームの状態を表します。ボールの位置と速度を保持します。
type Game struct {
	x, y   float64
	vx, vy float64
}
```

次に、新しいゲーム状態を作成する関数を定義します。ボールの初速度はランダムに設定されます。

```go
// NewGame は新しいゲーム状態を作成します。ボールの初期速度はランダムです。
func NewGame() *Game {
	rand.Seed(time.Now().UnixNano())                 // 乱数のシードを現在の時間（ナノ秒）に設定
	vx, vy := rand.Float64()*4-2, rand.Float64()*4-2 // ランダムな速度を生成
	return &Game{vx: vx, vy: vy}                     // 新しいゲーム状態を返す
}
```

ゲームのロジックを更新する関数を定義します。ボールの位置を更新し、壁への衝突を検出します。壁と衝突した際には、ボールの速度をランダムに変更します。

```go
// Update はゲームのロジックを更新します。ボールの位置を更新し、壁への衝突を検出します。
// 壁と衝突した際には、ボールの速度をランダムに変更します。
func (g *Game) Update() error {
	g.x += g.vx // X軸方向に速度分動かします。
	g.y += g.vy // Y軸方向に速度分動かします。

	// ボールが壁に衝突したら、反射させるだけでなくランダムな方向に速度を変更
	if g.x <= 0 || g.x+ballSize >= screenWidth {
		g.vx = rand.Float64()*4 - 2 // X軸の速度をランダムに設定
	}
	if g.y <= 0 || g.y+ballSize >= screenHeight {
		g.vy = rand.Float64()*4 - 2 // Y軸の速度をランダムに設定
	}

	return nil // エラーはないのでnilを返す
}
```

そして、ゲームの状態を描画する関数を定義します。背景を黒で塗りつぶし、ボールを白で描画します。

```go
// Draw はゲームの状態を描画します。背景を黒で塗りつぶし、ボールを白で描画します。
func (g *Game) Draw(screen *ebiten.Image) {
	screen.Fill(color.RGBA{0, 0, 0, 255}) // 画面を黒で塗りつぶします。

	// 矩形（ボール）を描画します。
	ebitenutil.DrawRect(screen, g.x, g.y, ballSize, ballSize, color.RGBA{255, 255, 255, 255})
}
```

次に、画面の解像度を制御する関数を定義します。ここでは固定の画面解像度を返しています。

```go
// Layout は画面の解像度を制御します。ここでは固定の画面解像度を返しています。
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
	return screenWidth, screenHeight // 定義した画面解像度を返す
}
```

最後に、ゲームの開始地点となるmain関数を定義します。ウィンドウサイズとタイトルを設定し、ゲームを開始します。

```go
// main関数はゲームの開始地点です。ウィンドウサイズとタイトルを設定し、ゲームを開始します。
func main() {
	ebiten.SetWindowSize(screenWidth, screenHeight) // ウィンドウのサイズを設定
	ebiten.SetWindowTitle("ボールランダム移動")              // ウィンドウのタイトルを設定

	if err := ebiten.RunGame(NewGame()); err != nil {
		log.Fatal(err) // ゲームの実行中にエラーが発生した場合、ログを出力してプログラムを終了
	}
}
```

以上
[Android]エラー： java.lang.SecurityException: No active admin

## 環境

- Andoroid 13(SDK 33)
- Google Play Console

## 状況

Google Play Consoleのリリース前レポートで発生しました。

エラーメッセージ

> java.lang.SecurityException: No active admin ComponentInfo{com.google.android.apps.mtaas.deviceadmin/com.google.android.apps.mtaas.deviceadmin.DeviceAdminReceiver}

スタックトレース
```
Exception Process: com.google.android.apps.mtaas.deviceadmin, PID: 6046
java.lang.SecurityException: No active admin ComponentInfo{com.google.android.apps.mtaas.deviceadmin/com.google.android.apps.mtaas.deviceadmin.DeviceAdminReceiver}
at android.os.Parcel.createException (Parcel.java:2088)
at android.os.Parcel.readException (Parcel.java:2056)
at android.os.Parcel.readException (Parcel.java:2004)
at android.app.admin.IDevicePolicyManager$Stub$Proxy.setNetworkLoggingEnabled (IDevicePolicyManager.java:15305)
at android.app.admin.DevicePolicyManager.setNetworkLoggingEnabled (DevicePolicyManager.java:11627)
at com.google.android.apps.mtaas.deviceadmin.EnableNetworkLogging.onStart (EnableNetworkLogging.kt:27)
at android.app.Instrumentation$InstrumentationThread.run (Instrumentation.java:2209)
Caused by android.os.RemoteException: Remote stack trace:
at com.android.server.devicepolicy.DevicePolicyManagerService.getActiveAdminWithPolicyForUidLocked (DevicePolicyManagerService.java:3259)
at com.android.server.devicepolicy.DevicePolicyManagerService.getActiveAdminOrCheckPermissionForCallerLocked (DevicePolicyManagerService.java:3155)
at com.android.server.devicepolicy.DevicePolicyManagerService.getActiveAdminForCallerLocked (DevicePolicyManagerService.java:3136)
at com.android.server.devicepolicy.DevicePolicyManagerService.enforceCanManageScopeOrCheckPermission (DevicePolicyManagerService.java:7218)
at com.android.server.devicepolicy.DevicePolicyManagerService.enforceCanManageScope (DevicePolicyManagerService.java:7204)
```

## 原因

こちらはGoogleのテストフレームワークにおけるバグです。(1)
https://issuetracker.google.com/issues/160907013?pli=1#comment144

## 対応方法

このエラーはアプリ本体には無関係ですので対応の必要がありません。
よって、無視してリリース作業を続けます。

## 補足

　リリース前レポートでエラーはあくまでアプリ改善の分析情報と推奨事項の提示という位置付けになります[^1]。そのため、エラーがあっても以降のリリース作業で進めなくなることはありません。開発者が問題ないと判断できればリリースを継続することができます。実際にエラーが表示されてもそのままリリースできました。
　また、このエラーが発生するか否かは運です。同一のビルドで再度テストするだけで消えることがほとんどです。

## 終わりに

レポートを品質向上に活用することは大切ですが、ユーザーの価値に繋がる範囲を見極めたいですね。
本件を作成するにあたり下記を参考にさせていただきました。
- [Getting 'No active admin' SecurityException in Android 10 - how to resolve?](https://stackoverflow.com/questions/76391747/getting-no-active-admin-securityexception-in-android-10-how-to-resolve)


[^1]: [Play Console リリース前レポートを使って問題を特定する](https://support.google.com/googleplay/android-developer/answer/9842757
)
学習について

## 最近について
私生活がバタバタしていて投稿の間隔が少し空きましたが独学での学習は継続しています!
現在はPHPについて学習を進めているので現在学習している内容や学習が完了
した内容について不定期ではありますが、記事を投稿して行けたらと思います。


新しい言語を独学で学習するのは大変ですが、楽しいって気持ちの方が
勝ってるのが幸いです。

では、次の投稿までsee you again!
Github ActionsでPRの差分ファイルだけをZIP化してDLする

## これは

案件の納品時、修正したファイルだけzip化して欲しいと要求されたため、ワークフロー化する方法を探ってみました。
ワークフローで差分を抽出する方法は色々記事が上がってるものの、微妙にユースケースが違ったので参考にしつつカスタマイズしました。

## tl;dr

ざっくりこんな感じで出来ました。
もっとスマートに書けそうですが一旦動いたので。。。


```yml
name: create-zipped-diff-files

on:
  pull_request:
    branches: [master] # zip化対象のブランチを指定
    types: [closed]
env:
  diff_path: 'src/*' # 差分比較対象のディレクトリを指定

jobs:
  run-actions:
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true && github.head_ref == 'release' # headがブランチ名がreleaseの時に実行
    steps:
      - name: switch to pull request branch
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: create delivery zip
        run: |
          diff_files=$(git diff --name-only HEAD^ -- "${diff_path}" --diff-filter=ACMR) # baseのHEADとその一つ前を比較
          git archive HEAD ${diff_files} -o delivery.zip
      - name: upload zip
        uses: actions/upload-artifact@v3
        with:
          name: delivery
          path: ./delivery.zip

```

## 作業イメージ

上記ワークフローを使用する際は

- `release`→`master`にPRをmergeすることで実行
- 差分があれば実行完了後にArtifactsからZIPをダウンロードできる

という感じになります。

※ブランチ名とかパス名は任意です

## ハマったこと

Github Actions素人なので構文の調査に結構時間がかりました。

特に当初はbaseブランチとheadブランチを比較して差分を取る方針で考えていたのですが、冷静に考えればmergeをトリガーにしている都合上、merge後は両者で差分が取れない（存在しない）ことに結構後になって気づきました。

ブランチの取得方法については下記記事を参考にさせていただきました。
[GitHub Actions の Workflow 実行内で Ref（ブランチ名）を取得する方法](https://dev.classmethod.jp/articles/how-to-get-a-ref-branch-within-a-workflow-execution-in-github-actions/)

以上です。
2023年8月 AIOps 定点観測

# 概要
AIOps の動向や学習のため、Google で AIOps を検索した結果を記録している記事です。

# 2023年8月1日の検索結果 (リスティング広告)

![20230801_AIOps_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345504/a3898f68-c749-8172-08a2-0a9598374456.png)

* PureStorage
* IBM
* Dynatrace
* Splunk

今月は PureStorage がトップです。 IBM と Dynatrace が続いています。



先月の結果はこちら --> [2023年7月 AIOps 定点観測](https://qiita.com/mkodera/items/65e4ea24ddbec034ecfa)

# 2023年8月1日の検索結果 (広告以外)

![20230801_AIOps_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345504/150097ad-4c8d-cbea-a6ed-16328b67cf37.png)


* HPE
* Splunk
* IBM
* 日立製作所
* ServiceNow


広告以外では HPE が強調スニペットでトップに表示されています。 その後に Splunk、IBM が続いています。1～5番目まで先月と全く同じです。

先月の結果はこちら --> [2023年7月 AIOps 定点観測](https://qiita.com/mkodera/items/65e4ea24ddbec034ecfa)
初めてVoltaっちゅうものを触ってみた。

# 初めてVoltaっちゅうものを触ってみた。

初めてVoltaを触ってみた時の感想や、備忘録的なものをつらつらと書いていきます。

参考： https://dev.classmethod.jp/articles/node-version-volta/

## インストール

bashを使っているので、

```bash
$ curl https://get.volta.sh | bash
```

> windowsの場合はインストーラーがあるらしいけど、↑で問題なくできたし、個人的には楽な気がする？



## Node.jsをインストールする

今回は、`v18.16.0`を使うのでこれを指定する。

```bash
$ volta install node@18.16.0
$ node --version
v18.16.0
```

マイナーバージョンは省略できるみたい。

```bash
$ volta install node@18
```



## Voltaのpinを使って、ディレクトリ毎にNode.jsのバージョンを固定できるらしい

```bash
$ mkdir volta-test-node-v18.16.0
$ cd volta-test-node-v18.16.0
$ npm init
$ volta pin node@18.16.0
$ node --version
v18.16.0
```
スッキリわかるJava入門 第３版を読んでみて- part１ -プログラムの書き方

# はじめに
4月から入社して早くも３ヶ月が過ぎ本格的に業務も始まり自分も業務を任されました！
早速対象のコードを読んでみると....
何が書いてあるかわからない(´・ω・)
ということで __スッキリわかるJava入門第３版__ で勉強しようと思います！
全１８章＋付録３つを各章ごとにアウトプットとしてまとめていければと思います（＾ω＾）

## 第１章　プログラムの書き方
### 1.開発の流れ
1.ソースコードの作成
　人が読める状態のプログラム＝ __ソースコード__
　ソースコードを書き込んだファイル＝ __ソースファイル__
2.コンパイル
　ソースファイル ⇨ クラスファイルに変換
　ソースコードの各命令をコンピュータが実行しやすいように変換された __バイトコード__ が詰まっている
3.実行
 　__インタプリタ__ にバイトコードの変換と実行を依頼
　__JVM__ がバイトコード⇨マシンコードに変換

### 2.Javaプログラムの基本構造
・__ブロック__ `｛｝`  
　外側のクラスブロックと内側のメソッドブロックによる二重構造を持っている

・Javaソースファイルの名前
　ソースファイル名　⇨ ファイル名.java
　※ファイル名は大文字で始める

__・大切な意識__
1.✖️「上から下」⭕️「外から内」
　　ブロックを開いてすぐ閉じる⇨習慣化
2.読みやすいコードを記述する
　　インデント　字下げでブロックを見やすくする 
　　コメント
　　/**/　複数行
　　//　単一行
3.mainメソッドの中身
　　文
　　｜
　　1.変数宣言の文
　　2.計算の文
　　3.命令実行の文
### 3.変数宣言の文
　変数＝データをかくのするための箱
　　型　変数名;
　変数の命名での注意事項
　・予約語を使ってはいけない
　・すでに利用している名前を再度利用できない
　・大文字小文字、全角半角の違いは区別される
　・小文字で始まるわかりやすい名前が望ましい
### 4.データ型
プログラムで扱うことのできるデータの種類＝ __データ型(型)__
基本の９種類
整数　　byte 　short　　⭐️int 　　long
小数　　float　double
真偽値　boolean
文字　　char
文字列　⭐️String

定数
内容がプログラムの動作中に書き換わらないようにする
final　型　定数名　＝　初期値;
※定数名はすべて大文字を用いる
## まとめ
この章で特に大切にしたいと思ったのは読みやすいコードを記述するというところでした。
インデントでブロックを見やすくしたり、コメントを記述して誰が読んでもすぐ理解できるようなコードを書いていきたいと思いました。
その他には命名規則に対して考えて名前づけをしてなかったので気をつけたいと思います！

こんな形でアウトプットの代わりに章ごとにまとめを出していければと思います
ではまた〜　＊˙︶˙＊)ﾉ"
[ServiceNow] FormのExportメニューをRole別Table別で表示・非表示を制御する

# はじめに

ServiceNowのFormでは、Context MenuのExportから、対象レコードの情報をPDFで出力することができます。
しかし、Userのような機微な情報は、PDF化を禁止したいという要望もあるかもしれません。
ここでは、sys_userテーブルを対象に、admin以外は、Context MenuのExportを非表示にする方法を取り上げます。

# UI Macroの設定

FormのコンテキストメニューはUI Macroで制御されています。
All > System UI > UI Macrosから、「context_form_header」を開きます。
XMLの102行目に以下のコードを見つけてください。
（環境によっては位置が異なるかもしれません。）

```HTML:XML
        	gcm.addMenu("${gs.getMessage('Export')}", mExport, "context_exportmenu");
```

この行を、次のようにif文でネストします。
ref変数には、OOTBで対象のTable名が格納されています。
今回はこれを活用して、「admin以外はsys_userのFormのExportを表示しない」という要件を実現しています。

```HTML:XML
			<j2:if test="$[${gs.hasRole('admin')} || ${ref != 'sys_user'}]">
        	gcm.addMenu("${gs.getMessage('Export')}", mExport, "context_exportmenu");
			</j2:if>
```
Tableau Cloudへユーザを追加

## 前提
Tableau CloudサイトへのTableauユーザーの追加する手順を記載します。
## 参考ドキュメント
https://help.tableau.com/current/online/ja-jp/sites_addusers.htm


## 手順
1.Tableau Cloud サイトにサインインし、[ユーザー] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562390/5c324238-9932-d043-23b7-4fc88f560931.png)
2.[ユーザー] ページで、[ユーザーの追加]、[電子メール アドレスの入力] の順にクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562390/5cb0623d-6718-3240-8426-43ffa830024f.png)
3.既定の電子メールアドレスとパスワードによる認証をユーザーが行うようにするには、[Tableau]を選択します。(ユーザーの追加後、いつでも [ユーザー] ページに移動して認証タイプを変更できます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562390/b146a73c-ca2d-6e08-a1d6-c6cef38e552b.png)
4.[メール アドレスの入力] ボックスで、ユーザーのメール アドレスを入力します。複数のユーザーを追加する場合は、各アドレスをセミコロンで区切ります。
たとえば、「tdavis@example.com; jjohnson@example.com; hwilson@example.com」のように入力します。
5.ドロップダウンリストからサイトロールを選択して、追加するすべてのユーザーにサイト ロールを割り当てます。
6.[ユーザーの追加] をクリックします。
Excel表のカテゴリの2行目以降を見えなくする

カテゴリ
商品
商品
商品

みたいに並んでいるとき、1行目の商品だけ表示させて、2行目以降は表示されないようにしたい。という場合があります。（見栄えを良くするため）

この場合、条件付き書式設定で「指定の値に等しい」を選択、表の一番上のセル（上の表がA1から始まっているならA1、$A$1ではない）を入れて、書式を「ユーザー設定の書式」を選択。フォントの色を背景色を同じにしてしまえばよい。

※ChatGPTに聞いたらそんな事できないと言われたので、自分で解決した手順のメモ
launch4jをコマンドラインで実行する方法

# はじめに
本記事ではjarファイルからexeファイルを作成するツールであるlaunch4jをコマンドラインから呼び出す方法を説明します。

# 環境
* Windows11

# 1. launch4jcを利用可能にする
launch4jをコマンドラインから呼び出せるlaunch4jcをダウンロードしてPATHを通します。

1. Launch4jの公式サイトにアクセス
    https://launch4j.sourceforge.net/
2. 左のタブの「Download」からダウンロードページヘ移動して`launch4j-x.xx-win32.zip`をダウンロード
    ![スクリーンショット 2023-08-01 121803.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/d5acec93-e4c1-0cbd-2e5d-eb21e0a638ea.png)

2. ダウンロードしたzipを展開して適当な場所に移動
3. lanuch4jcがあるディレクトリ(`launch4j-x.xx-win32\launch4j\`)にPATHを通す
3. PowerShellなどのシェルで以下のコマンドを実行してlaunch4jのGUIツールが起動すればOK
    ```powershell
    launch4jc
    ```

# 2. jarファイルからexeファイルに変換
launch4jのGUIツールで作成したconfig.xmlをlaunch4jcに渡すことで変換を行うことができます。

今回は例としてtarget.jarをtarget.exeに変換してみます。

1. launch4jcコマンドなどでlaunch4jのGUIツールを開く
1. Output fileに出力先ファイル、Jarに変換元jarファイルを入力
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/d48a9852-4605-1368-65bb-56d50785cb7f.png)

2. 左上のSaveボタンをクリックして設定を保存。今回はconfig.xmlとして保存
    ![スクリーンショット 2023-08-01 130706.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608343/a8202829-6b29-175c-0e9f-92fa4634d0cd.png)
3. 設定ファイルの場所を引数に与えてlaunch4jcを実行することでexeファイルが生成される
    ```powershell
    launch4jc .\config.xml
    ```

# まとめ
本記事ではlaunch4jをコマンドラインで実行する方法を説明しました。
設定ファイルはXMLで記述されているため、プログラムから設定を書き換えて実行することで様々な応用ができると思います。





# 参考文献
* [launch4j - How to run lauch4j in command line? - Stack Overflow](https://stackoverflow.com/questions/19418738/how-to-run-lauch4j-in-command-line)
UE5 Github版エンジンのProjectをEpicGameLauncherに登録する方法


C:\Users\UserName\AppData\Local\UnrealEngine\5.2\Saved\Config\WindowsEditor\EditorSettings.ini
のRecentlyOpenedProjectFilesにuprojectの登録を行う


RecentlyOpenedProjectFiles=(ProjectName="D:/ue/XXXXX/XXXX.uproject",LastOpenTime=2023.06.29-14.56.49)

こちらを参考にしました。
https://twitter.com/shop_0761/status/794574176723947520?s=20
Android studio Export Project復元に注意

# Exportしたデータを復元しなければならない
- 何かやらかしてBuild不可になった
- やっぱりやらかして元に戻らなくなった。
- Exportはちゃんとできている

:::note info
メニュー（Windows）
File > Export で任意のフォルダにzipデータが保存されます。
:::


# 復元しないじゃないか！

**調べた情報**
> 復元
>> Zipファイルを任意のフォルダに解凍する。
>> File＞New＞Import Projectでインポートする。

- 確かに環境は戻ったように見えます・・・

## Buildできない！！
- build.gradleもなんか前と違う。一つしかない！
- 一旦新しいジェクトを作ってみました。


:::note warn
確認
何かおかしいところを確認します。
:::

# (解決策)そのまま解凍してAndroid studio を開かない！

:::note info
- 新しいプロジェクト：プロジェクト名＞app
- 保存したZip名>プロジェクト名＞app
:::

**ハマリポイントは以下の通り**
- プロジェクト名がzip の中に入っているのに気づかない
- プロジェクト名が小文字でも大文字始まりになっている

1. 冷静になってC:\Users\ユーザ名\AndroidStudioProjectsへ移動
1. 解凍されたファイルのフォルダ名をリネームするか新たに以前のプロジェクト名のフォルダを作る（ない場合）
1. 解凍されたフォルダのプロジェクト名より下の階層のファイル・フォルダをプロジェクト名のフォルダへ移動
1. Android studio を起動

## 自動で配置を戻してくれるみたいだ
![fukugen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155177/42007971-5365-e29e-6705-ec6dc8e2d167.png)

:::note info
インフォメーション
- そのまま解凍してしまうと頭がZIPファイル名になっていました
- Manifest.xmlの場所が違う場所にあり、パスが通ってないよ～と真っ赤になっていました
:::

# 確認
- Fileメニューに Syncメニューがある。
- アンダーメニューにProblemが表示されている
- なにより、Make buildがグレーアウトしてない。

**なんとも不慣れなツールで手間取りました**
左右要素は両端寄せ・中央要素を親に対して左右中央寄せ

左右のボタンのwidthの影響を受けずに親に対して左右中央寄せする実装方法に少し迷ったのでメモ。

# 結論

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="xxQmWwZ" data-user="Yuya-SSK" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/Yuya-SSK/pen/xxQmWwZ">
  Center alignment with parents</a> by Yuya Sasaki (<a href="https://codepen.io/Yuya-SSK">@Yuya-SSK</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

ポイントは以下。
```
  display: grid;
  grid-template-columns: 1fr auto 1fr;

  &__left-area {
    display: flex;
    justify-content: flex-start;
  }

  &__center-area {
    display: flex;
    justify-content: center;
  }

  &__right-area {
    display: flex;
    justify-content: flex-end;
  }
```
それぞれのボタンを内包する３つの領域を作り、`grid-template-columns`で均等配置。
各領域に対して個別に寄せ設定。
UnityのLocalizationでアプリ名をスクリプトで変更する方法

# 課題
Localizationの設定画面からMetadataを編集すればアプリ名は設定できる。
検証環境と本番環境でアプリ名を変えたい。
検証環境と本番環境のビルドはCI/CDでコマンドから実行している。
アプリ名をビルドスクリプト上で変更したい。

# 方法
Localizationのテーブルとエントリーは事前に作っておく。
以下のコードでiOSとAndroidのアプリ名を変更する。
これをビルド前に実行すれば良い。

```c#
using UnityEngine.Localization.Settings;

var tableName = "TableName";
var appNameKey = env.Contains("production") ? "AppNameProduction" : "AppNameStaging";
var iosMetadata = LocalizationSettings.Metadata.GetMetadata<UnityEngine.Localization.Platform.iOS.AppInfo>();
var androidMetadata = LocalizationSettings.Metadata.GetMetadata<UnityEngine.Localization.Platform.Android.AppInfo>();
iosMetadata.ShortName = new LocalizedString(tableName, appNameKey);
iosMetadata.DisplayName = new LocalizedString(tableName, appNameKey);
androidMetadata.DisplayName = new LocalizedString(tableName, appNameKey);
```
PHPMyAdminにログインできない場合の対処法

いつも通りXAMPPを使用してPHPMyAdminを使用としたら、以下のように表示された。
解決するのに少し時間がかかったのでメモ書き。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3323019/67c0c1c7-cf16-45e0-f484-d085d93c15a8.png)

## どこが原因か探る
・MySQLはログインできるか確認する。
＜確認方法＞
①XAMPPのコントロールパネルでShellを開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3323019/bb21046a-f68b-833d-dfe6-dc75f827afa1.png)
②Shellに以下のコマンドを入力する。
```
mysql -u root -p
```
③パスワードを聞かれたら、パスワードを入力してEnterを押す。
④ログインできない場合は以下を試す。

・MySQLにパスワードありでログインできなかった場合
①XAMPPのコントロールパネルで、 MySQL > Config > my.ini　を開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3323019/eaf831e3-d7ce-d066-08b1-b5a313dcd795.png)
②`[mysqld]`の下に以下を入力する。
　以下を入力することでパスワードなしでMySQLにログインが可能になる。
```
skip-grant-tables
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3323019/e7a5b9fa-acd4-028a-79fc-6d644efe661a.png)
③Shellに以下のコマンドを入力する。
```
mysql -u root
```
④ログイン出来たら、Shellに以下を入力する。
　`use mysql`を指定することで、パスワードなどが登録されたデータベースを使用する。
```
use mysql
flush privileges;
```
⑤同じくShellに以下を入力する。
　`new_password`に任意のパスワードを入力する。
```
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';
```
⑥MySQLのmy.iniに入力した`skip-grant-tables`　を削除し、
　XAMPPのMySQLを再起動する。
⑦Shellを起動し、以下を入力してログイン出来たらOK。
```
mysql -u root -p
```

## PHPMyAdminのパスワードを変更する。
①C:\xampp\phpMyAdmin\config.inc.php　の以下に設定したパスワードを入力する。
```config.inc.php
$cfg['Servers'][$i]['password'] = '任意のパスワード';
$cfg['Servers'][$i]['controlpass'] = '任意のパスワード';
```
②PHPMyAdminにログインする。ログイン出来ればOK。

## 参考サイト
MySQLでパスワードを忘れた場合の対処法
https://blog.senseshare.jp/forget-rootpassword.html

phpMyAdminにログインできなくなった時の解決方法
https://masafumi-blog.com/what-to-do-if-you-cant-log-in-to-phpmyadmin
【ChatGPT無】で四文字熟語（大変）

:::note info
本記事は、ChatGPT（無料版）で
行なっております。GPT4での確認
はできておりません。あしからず。
:::

　どうも、カーブミラーです。

　今日は、曇りで、
　　比較的、涼しいですね。
　　　ありがたいです。
***
　さて、今回は、
　　四文字熟語です。

　5個くらいならば
　　たぶん、大丈夫なのでしょう。

　でも、50個となると、
　　おしゃべりChatGPTさんが
　　　我慢できなくなるみたいw

　いろいろなだめすかしながら、
　　やった結果がこちらです。
```Text:四文字熟語50個の結果
処理結果のリストアップです：

1. 一期一会
2. 一網打尽
3. 一石二鳥
4. 七転八起
5. 不言実行
6. 九死一生
7. 五里霧中
8. 六根清浄
9. 八方美人
10. 千載一遇
11. 南船北馬
12. 口車に乗る
13. 四面楚歌
14. 天衣無縫
15. 奇想天外
16. 孤立無援
17. 心機一転
18. 悪戦苦闘
19. 才色兼備
20. 断腸の思い
21. 明鏡止水
22. 有名無実
23. 杞憂忌憚
24. 束手束足
25. 東奔西走
26. 栄枯盛衰
27. 活殺自在
28. 清廉潔白
29. 漁夫の利
30. 無我夢中
31. 無念無想
32. 片山落ち
33. 百発百中
34. 真実大白
35. 真偽不明
36. 石火光
37. 窮鼠猫を噛む
38. 紆余曲折
39. 自画自賛
40. 興味津々
41. 莫逆の友
42. 見栄晴らし
43. 見当違い
44. 試行錯誤
45. 豪華絢爛
46. 質実剛健
47. 転嫁禍根
48. 迫真の演技
49. 進退繁栄
50. 飛躍発展

処理結果：

a. 重複した熟語を削除：なし（元のリストに重複がないため）
b. ひらがなを含んだ熟語を削除：なし（全て漢字のみで構成されている）
c. 漢字のみではない熟語を削除：なし（全て漢字のみで構成されている）
d. 四文字ではない熟語を削除：なし（全て四文字で構成されている）
```

　ChatGPTさん、
　　嘘吐きですw

　とりあえず、
　　Promptをどうぞ。

```Text:四文字熟語Prompt
# 目的
四文字熟語のリストアップ

# 四文字熟語の例
一期一会
一網打尽
一石二鳥
七転八起
不言実行
九死一生
五里霧中
六根清浄
八方美人
千載一遇

# 注意点
プログラムコードは不要。
すぐに出力しない。

# 処理手順
1. 四文字熟語は、漢字のみを4つ使用して構成される熟語です。ひらがなを含んではいけません。
2. ルールに合致する四文字熟語を50個、箇条書きでリストアップします。
3. その後、リスト内で以下の処理を行います。
   a. 重複した熟語を見つけ出し、削除します。
   b. ひらがなを含んだ熟語を見つけ出し、削除します。
   c. 漢字のみではない熟語を見つけ出し、削除します。
   d. 四文字ではない熟語を見つけ出し、削除します。
   e. abcdを再度、実行する。削除できなくなったら、ループを抜ける。
3.上記の結果を箇条書き（番号付き、説明不要）でお願いします。
```

　抽出結果を
　　ソートさせてもみましたけど、
　　　それらしくなるけど、
　　　　ダメでした。

　おそらく、
　　手はある、
　　　のではないかとは
　　　　思うのですが……。

　期待薄かな？
『Gitの強力な助っ人、サブモジュールの使い方を完全解説！』



---

# はじめに

Gitはバージョン管理システムとして、ソフトウェア開発者にとって不可欠なツールです。特に、複数のプロジェクトやライブラリが絡むとき、Gitのサブモジュール機能は大変便利です。今回は、このGitのサブモジュールの基本的な使い方を紹介します。

---

## 目次
1. [サブモジュールとは](#サブモジュールとは)
2. [サブモジュールの追加](#サブモジュールの追加)
3. [サブモジュールの初期化とアップデート](#サブモジュールの初期化とアップデート)
4. [サブモジュールの変更の取り込み](#サブモジュールの変更の取り込み)
5. [まとめ](#まとめ)

---

# サブモジュールとは

Gitのサブモジュールは、ひとつのリポジトリに別のリポジトリをネストするための機能です。これにより、独立したプロジェクトを一つの親リポジトリで管理することができます。

# サブモジュールの追加

それでは、実際にサブモジュールを追加してみましょう。

まずは、以下のコマンドをターミナルで実行します。

```bash
git submodule add <repository>
```

ここで `<repository>` はサブモジュールとして追加したいリポジトリのURLです。

例えば、あるリポジトリを `my-submodule` という名前でサブモジュールとして追加したい場合は次のようにします。

```bash
git submodule add https://github.com/user/repo.git my-submodule
```

これでサブモジュールが追加され、そのリポジトリの内容が `my-submodule` ディレクトリ下にクローンされます。

# サブモジュールの初期化とアップデート

次に、サブモジュールを初期化し、アップデートします。

そのためには、以下のコマンドをターミナルで実行します。

```bash
git submodule init
git submodule update
```

これらのコマンドにより、サブモジュールの初期化と、サブモジュールの最新のコミットをチェックアウトすることができます。

# サブモジュールの変更の取り込み

サブモジュールのリポジトリで変更があった場合、その変更を親リポジトリに取り込むことが重要です。

以下のコマンドをターミナルで実行します。

```bash
git submodule update --remote
```

これにより、サブモジュールの最新のコミットが親リポジトリに取り込まれます。

# まとめ

以上がGitのサブモジュールの基本的な使い方となります。サブモジュールはGitの高度な機能であり、慎重な取り扱いが必要ですが、使いこなすことでソフトウェアの開発効率が大幅に向上します。

次回は、サブモジュールを更に活用するためのテクニックを紹介しますので、お楽しみに！

--- 

それでは、Happy coding :)
DjangoRestFrameworkで、追加の情報をレスポンスに含める

:::note info
間違いがあるかもです。修正点は教えていただくと幸いです。
:::
## :v: 使用しているライブラリのバージョン
- Django==4.2.3
- djangorestframework==3.14.0
- djangorestframework-api-key==2.3.0
## したいこと
modelに記述されていない、追加の情報をquerysetに含めたい。
## DRF(Django Rest Framework)を使わないとき
viws.pyにこのように書くだけで実現できる
``` views.py
class userView(ListView):
    template_name = "notes.html"
    # 一回で読み込む量
    paginate_by = 10

    def get_queryset(self):
        username = self.kwargs["user"]
        user = User.objects.get(username=username)
        noteList = (
            Post.objects.filter(user_id=user.id)
            .order_by("-posted_at")
            .annotate(
                is_liked=Exists(
                    likes.objects.filter(
                        likes_note=OuterRef("id"), like_user=self.request.user.id
                    )
                )
            )
        )
```
.annotateで、is_likedという値を渡すようになっている。
もちろん、テンプレートの渡される！！！

## ではDRFを使うと。
:::note info
DRFの下準備は別の記事を参照ください
:::
レスポンスのJSONにis_likedが含まれなくなっている
```json

{
    "results": [
        {
            "id": 106,
            "note": "改行\r\n改行",
            "posted_at": "2023-08-01T09:03:24.737931+09:00",
            "likes_count": 0,
            "user": 1
        },
        {"//": "以下略"}
        
```
## 解決法
    serializer.pyにフィールドを追加で書く
```serializer.py
    class NoteAPI(serializers.ModelSerializer):
    is_liked = serializers.BooleanField()

    class Meta:
        model = Post
        fields = "__all__"
```
こうすることによって、フィールドが動的に作られ、渡される。
Qiitaの記事作成でリンクを埋め込む方法

先ほどQiitaにて記事を制作する際にリンクの埋め込み方を調べたので、こちらの記事に残します。

誰かの参考になれば嬉しいです😊

# リンクの埋め込み方法

結論から言うと下記の表記でリンク埋め込みが出来ます。

```
(空白)
埋め込みたいURL
(空白)
```

このような感じです。☟

https://qiita.com/


# 他のリンク表記方法

参考までにこの記事の後の方にその他のリンク表記法を載せますが、個人的には上の方法が一番簡単で見やすいので良いと考えてます。


```
[表示する文字列](飛ばす先のURL)
```



このようになります☟

[Qiita](https://qiita.com/)



[表示する文字列]の部分には好きな文字列を表記できますが、単に外部のリンクを埋め込みたいだけであれば前に載せたURLの上下に空白を入れるだけのもので事足りそうですね。
TypeScriptでA*アルゴリズムを実装してみた

TypeScriptでA*アルゴリズムを実装してみました。

※ 実装の詳細については、後日追記します。

実コスト(actualCost)と推定コスト(heuristicCost)を用いてソートします。
```typescript
class AStarNode {
  private cell: Cell;
  private actualCost: number;
  private heuristicCost: number;
  private prev: AStarNode;

  constructor(cell: Cell) {
    this.cell = cell;
    this.actualCost = Number.MAX_VALUE;
    this.heuristicCost = 0;
    this.prev = null;
  }

  public getCell(): Cell {
    return this.cell;
  }
  public getActualCost(): number {
    return this.actualCost;
  }
  public getHeuristicCost(): number {
    return this.heuristicCost;
  }
  public getPrevNode(): AStarNode {
    return this.prev;
  }
  public setActualCost(actualCost: number): void {
    this.actualCost = actualCost;
  }
  public setHeuristicCost(heuristicCost: number): void {
    this.heuristicCost = heuristicCost;
  }
  public setPrevNode(prevNode: AStarNode): void {
    this.prev = prevNode;
  }
}
```

↓の優先度付きキューと組み合わせて使います。

https://qiita.com/kh_kmgch/items/ea541ef59a813becbb10
TypeScriptで優先度付きキューを実装してみた

TypeScriptで優先度付きキューを実装してみました。

※ 実装の詳細については、後日追記します。

```typescript
type HeapNode<T> = {
  data: T;
  priority: number;
};
```

```typescript
class HeapLibrary<T> {
  public comparator: (a: HeapNode<T>, b: HeapNode<T>) => boolean;

  constructor(comparator: (a: HeapNode<T>, b: HeapNode<T>) => boolean) {
    this.comparator = comparator;
  }

  public buildHeap(arr: HeapNode<T>[]): void {
    const middle: number = HeapLibrary.parent(arr.length - 1);
    for (let i = middle; i >= 0; i--) {
      this.heapify(arr, arr.length - 1, i);
    }
  }

  public heapify(arr: HeapNode<T>[], heapEnd: number, i: number): void {
    const l: number = HeapLibrary.left(i);
    const r: number = HeapLibrary.right(i);

    let biggest: number = i;
    if (l <= heapEnd && this.comparator(arr[l], arr[biggest])) biggest = l;
    if (r <= heapEnd && this.comparator(arr[r], arr[biggest])) biggest = r;

    if (biggest !== i) {
      const temp: HeapNode<T> = arr[i];
      arr[i] = arr[biggest];
      arr[biggest] = temp;
      this.heapify(arr, heapEnd, biggest);
    }
  }

  public static left(i: number): number {
    return 2 * i + 1;
  }

  public static right(i: number): number {
    return 2 * i + 2;
  }

  public static parent(i: number): number {
    return Math.floor((i - 1) / 2);
  }
}
```

```typescript
class PriorityQueue<T> {
  private heap: HeapNode<T>[];
  private heapLibrary: HeapLibrary<T>;

  constructor(arr: HeapNode<T>[], comparator: (a: HeapNode<T>, b: HeapNode<T>) => boolean) {
    this.heap = [...arr];
    this.heapLibrary = new HeapLibrary<T>(comparator);
    this.heapLibrary.buildHeap(this.heap);
  }

  public isEmpty(): boolean {
    return this.heap.length <= 0;
  }

  public top(): HeapNode<T> {
    return this.heap[0];
  }

  public pop(): HeapNode<T> {
    const temp: HeapNode<T> = this.heap[0];
    this.heap[0] = this.heap[this.heap.length - 1];
    this.heap.pop();
    this.heapLibrary.heapify(this.heap, this.heap.length - 1, 0);
    return temp;
  }

  public insert(t: HeapNode<T>): void {
    this.heap.push(t);
    let i: number = this.heap.length - 1;
    let parent: number = HeapLibrary.parent(i);
    while (
      parent >= 0 &&
      !this.heapLibrary.comparator(this.heap[parent], this.heap[i])
    ) {
      const temp: HeapNode<T> = this.heap[i];
      this.heap[i] = this.heap[parent];
      this.heap[parent] = temp;
      i = parent;
      parent = HeapLibrary.parent(i);
    }
  }
}
```
Vue.js入門　vue2.x->vue3.xへ

# はじめに
Vueを学習する必要がありました。
差し当たって、
- 定評がある（人気がある）
- 中古本で大量に出回って安く買える入門書

ということで、探したところ、以下の本が良さそうでした。

[技術評論社　「Vue.js入門 基礎から実践アプリケーション開発まで」(ISBN978-4-297-10091-9)](https://gihyo.jp/book/2018/978-4-297-10091-9)


https://gihyo.jp/book/2018/978-4-297-10091-9

# 中古本の難点

2018年に出版されているので、最新のバージョンではない、あるいは現在のトレンドから外れていたりします。

この本の場合、Vue2.x→Vue3.xへ破壊的変更がされているという難点があります。
本書は、Vue2.xで書かれています。

# Vue3.Xの書き方

Vue2.xの記述方式をVue3.xに替えたら特に問題ないでしょう？
ということで、最初に出てくるソースコードを書き換えてみたところ、以下のようになりました。
CDNの読み込み、インスタンス生成の記述方法から違いますね。

## Vue2.X

``` vue
<!DOCTYPE html>
<title>はじめてのVue.js vue2.x</title>
<script src="https://unpkg.com/vue@2.5.17"></script>

<div id="app"></div>

<script>
    new Vue({
        template: '<p>{{msg}}</p>',
        data: {msg:'hello!!'}
    }).$mount('#app')
</script>

```

## Vue3.X

``` vue
<!DOCTYPE html>
<title>はじめてのVue.js vue3.x</title>

<div id="app"></div>

<script type="importmap">
  {
    "imports": {
      "vue": "https://unpkg.com/vue@3.3.4/dist/vue.esm-browser.js"
    }
  }
</script>

<script type="module">
import { createApp } from 'vue'

createApp({
  template: '<p>{{msg}}</p>',
  data() {
    return {
      msg: 'hello world'
    }
  }
}).mount('#app')
</script>


```

# 本書の進め方

- Vue開発で必要なことが一通りわかるようになっているので、まず、Vue2.xで書いてある通りにやってみる。
- Vue3.X向けに書き直してみる。この書き直しで、自分で調べて努力するので、普通にすすめるより力がつくかもしれない・・・。


## 最後に
私自身は、Vue2.0のソースコードを改修する作業のために学習し始めたので、Vue3.Xにする必要ないんですけども。
Dockerイメージとは

Dockerは、Dockerイメージを用いて、コンテナ動作させる。
Dockerfileをビルドすることで、Dockerイメージが生成される。


**アプリケーション実行に必要な変数やコマンド、メタデータを含んだテンプレートファイルを、Dockerイメージと言う。**


イメージは、ディスクに格納された静的情報（起動前の状態）である。
**docker run**することで、イメージが起動され、コンテナが作られる。
実行中のコンテナは、メモリ上の動的情報（実行インスタンス）となる。

つまり。コンテナを実行するにはDockerイメージが必要となり、**実行状態のインスタンスを、コンテナ**という。
DockerでLaravelプロジェクトを起動するとき、「php artisan serve」をしない理由

Laravelは、apache（Webサーバ）と、publicフォルダ（Laravelを動かすフォルダ。**画面に描画される関連ファイルがしまわれているフォルダ**）を紐づけることで起動する。

php artisan serveはそれをする役割があるが、
Dockerの場合、nginxがその役割を自動的にやるので、artisan serveをしなくても起動する。
TortoiseGitで”git log --follow -p filename”相当のことをする方法

以下の手順でできる
１）普通にメニューからShow logで表示
２）ログが表示されたウィンドウの最下段にあるメニューの”Walk Behavior"をクリック
３）サブメニューの中にある"Follow renames"をクリック

設定で常時ONとかはできないっぽい
IBM Turbonomic動画集

# はじめに
実際のアプリを動かした時のデモや操作の動きを気軽に、いますぐ、サクッと見たい！
そんな要望から作っている動画をご紹介します！

特に本記事では、[APM](https://www.ibm.com/jp-ja/topics/application-performance-management?utm_content=SRCWW&p1=Search&p4=43700076154465038&p5=e&gclid=Cj0KCQjw1_SkBhDwARIsANbGpFtJCAvEYryzL3yLi1qqccFkp61oxFXbgOOW79JkT9PZh3OrqN0y-jIaArLyEALw_wcB&gclsrc=aw.ds)製品であるIBM Turbonomic(以降Turbonomic)に関する日本語の動画や記事のリンクをまとめて随時更新しています。
IBM Instana Observabilityについては[こちら](https://qiita.com/LgmQue/items/68773c9a14406f610509)を参照ください。
IBM Cloud Pak for AIOpsについてはこちら(準備中)を参照ください。

Turbonomicは[こちら](https://www.ibm.com/account/reg/jp-ja/signup?formid=urx-51901)からお試しいただけます。


## 10分以内の動画

|リンク|概要|長さ|
|:-|:-|:-|
|[Turbonomicの画面に表示されている通貨単位をドルから円に変更する方法](https://video.ibm.com/recorded/132403133)|TurbonomicのUI設定に関するデモ動画です。UI上に表示される各種金額の通貨単位を変更する方法をご紹介します。|1:07|
|[Turbonomicを使ってサステナブル・ダッシュボードを作成しよう](https://video.ibm.com/recorded/132920112)|TurbonomicとGrafanaを連携し、サステナブルダッシュボードを作成する方法をご紹介します。 |4:40&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
|[Microsoft Azure（Service Principal）とTurbonomicの連携方法](https://video.ibm.com/recorded/132413816)|Turbonomicの監視対象にMicrosoft Azure(Service Principal)を追加する方法をデモでご紹介します。|8:10|
|[Turbonomic 紹介＋デモ](https://video.ibm.com/recorded/132354468)|この動画では座学とデモに分けて、入門的な知識を紹介しております。 <dl><dt>・座学(3分) </dt><dd>-なぜTurbonomicが必要なのか？</dd><dd>-競合製品と比較した場合の優位性は？</dd><dt>・デモ(7分) </dt><dd>-アプリのリソース管理状況の表示と原因解析</dd><dd>-クラウド環境におけるコスト表示と是正提案</dd></dl>|9:33|
PowerAppsにおけるSharePointの5000件問題って何が問題なの？

2023/11/10追記
SharePointのリストの数が3万件ではなく3千万件だったので訂正しました。
すみませんでした。

# この記事で伝えたいこと
::: note info
そもそも、SharePointは ~~3万件~~ 3千万まで保存できるよ
でもSharePointで5000件以上「まとめて」処理したり等すると、エラーになっちゃうよ
PowerApps側ではこれに引っかからないようにPowerApps自体がもう制限かけてるよ
:::
::: note warn
独自調査の結果だったり、するので、そこは気をつけてください。
基本的には文末に全て(のはず)とつく前提で...。
:::
# 本題
さて、早速ですが皆様いわゆる5000件問題に頭を悩まされたことはありませんか。
私は直接的にはないですが...PowerAppsのデータベースとしてSharePointを採用しているので
悩まされかけて自分なりに調査結果をまとめてみました。

### 実際問題どうなの？
~~**SharePoint自体は3万件まで保存できる**~~
**3万件**じゃありませんでしたすみませんでした。
**3千万件**でした。~~増えたから許して~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676003/862966a9-881a-0118-e4c2-0b99993942f0.png)

えっ、じゃあ何が問題なの？といったところですが、5000件以上のものに対して一回で表示しようとしたりするとエラーになるようです。
実際のとこSharePointのリストのページでも何件かに分けて表示しているのがわかると思います。
この処理で5000件問題に対処しているわけですね。
ちなみに筆者はてっきり5000件以上保存したらもうやばいのかと思っていました。

* **Collectionの場合**
そもそもPowerApps側で設定のデータ行の制限というところで1~2000件の間で制限をかけている(デフォルトでは500件)のでCollectionではエラーになることはないでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676003/71715bf1-e71e-5c7b-b470-6e908bf8ee29.png)
※データ行の制限をデフォルトの500件にしている場合の話
ただし、デフォルトでは**IDが古い順に取ってきてしまう**のがすこしやっかいですが、
逆に言ってしまえば**ID等を新しい順に取ってくれば**最新から500件取得することができます。
(もしくは、普通にフィルターを掛けてあげるとか・・・)

* **SharePointを直接表示したい場合**
ギャラリーにコレクションを入れたりするとデータ行の制限以上に件数が取れます。
ですが、やはりいわゆる5000件問題に引っかかってしまうようです。
筆者環境ですと5100件まで取ったところで止まったり、そのままもっと行けたりしたので即座にエラーではないのかもしれませんが、この辺曖昧ではありますがなるたけ避けてあげるのが得策でしょう。
この場合、ページで分ける、取得する条件を指定する、上述のようにID等を新しい順に並び替えて5000件以降は諦める、そもそも5000件行かないように定期的に削除する。等の対処が必要になります。

# 委任とかの話
すみません、実は正確に言うとデータ行とかの話とかと5000件問題とか別の話なんですよね・・・
* ギャラリーにコレクションを入れたりするとデータ行の制限以上に件数が取れます。

ここの話ですね。
委任できるものはデータ行の制限等に引っかからず、5000件問題に引っかかります。
委任とはつまり、簡単に言うとデータベースに処理は任せるぜ！な感じです。
こっち側で処理する際にはデータ行の制限に引っかかります。
また、関数によって委任できるできないの違いがあります。
詳しくは自分も理解しきれていないのもあるので下記サイトにおまかせしようと思います。

Power Apps：結局「委任に関する警告」ってなに？
https://artisan.jp.net/blog/power-apps-canvas-apps-delegation-overview/
# あとがき
いかがでしたでしょうか。
冒頭でも書きましたが、筆者は5000件以上保存し始めた時点でエラーが起き始めるものだとばっかり勘違いしていました。
PowerAppsではギャラリーで5000件以上表示できるとしても基本的にスクロールの都度(多分100件ごと？)読み込まれるのであまり悩まされることは少ないのかもしれません。
PowerAppsで5000件もデータ一回で表示したいことってあんまりないですしね・・・。
~~そこまで来ると別のものを使ったほうがいい気がします。~~
# 参考
https://www.docswell.com/s/hrfmjp/5QNNN5-2022-05-09-124548#p25

https://idea.tostring.jp/?p=4088#:~:text=%E3%83%AA%E3%82%B9%E3%83%88%E3%82%84%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AB%E4%BF%9D%E5%AD%98,%E3%81%A6%E3%81%97%E3%81%BE%E3%81%86%E3%81%A8%E3%81%84%E3%81%86%E3%82%82%E3%81%AE%E3%81%A7%E3%81%99%E3%80%82

https://qiita.com/rnakamuramartiny/items/1ae737168d53da6c0487

https://note.com/arukt/n/n84ccf12296e8

https://artisan.jp.net/blog/power-apps-canvas-apps-delegation-overview/

https://learn.microsoft.com/ja-jp/office365/servicedescriptions/sharepoint-online-service-description/sharepoint-online-limits
一般ユーザーでSSHするときのPermission denied (publickey)の解決法

# SSHでPermission denied (publickey)エラー

サーバーに一般ユーザーを作成して、公開鍵暗号方式でSSHでログインしようとしたら`Permission denied (publickey)`と怒られてなかなか解決できなかった。

パスワードでログインはできるが、公開鍵だとダメ。

いろいろとググって以下の方法を試してみたが、ダメだった。

- 秘密鍵と公開鍵は正しいか？
  - 最初は秘密鍵のコピペミスかと思って、scpでコピーしたがそれでもダメ
- .ssh/configは正しいか？
  - 秘密鍵のパスもユーザー名もIPアドレスも間違いない
- 秘密鍵や.sshディレクトリのパーミッションはあっているか？
    - 秘密鍵は600、.sshディレクトリは700
- 公開鍵の所有者はログインしようとしている一般ユーザーか？
  - chownで所有者を変更後、lsで確認


# 結論

一般ユーザーではなく、rootの.sshディレクトリに公開鍵を置いていた。

一般ユーザーのホームディレクトリ直下に改めて.sshディレクトリと公開鍵を配置したところ、無事にログインできました。