Shellスクリプトを用いた個人開発環境の新規作成の自動化

# 目次
[対象](#-対象)
[できること](#2-できること)
[必要な知識](#3-必要な知識)
[実装](#4-実装)
[終わりに](#5-終わりに)

# 対象
以下のような軽めの目的で開発環境を作る際の作業の自動化を目指す。
- 個人開発
- プログラミング学習

普段個人的な開発や学習をする時に
- github
- vscodeのworkspace

を使う方が対象。これらを用いた開発環境を作るのに毎回手動であっちこっち行くのが面倒なので自動化した。

# できること
```shell
    $ setDE <ディレクトリ名>
```
- ディレクトリの作成
- git環境の作成
- vscode workspace の作成
- 作成したディレクトリの`ws`コマンド (後述) の追加

ができる。DE は development environment の略。

```
    $ gitinit <リポジトリ名>
```
git リポジトリを作成できる。毎回同じ設定で作成するのであれば、そこも自動化可能。

```shell
    $ mkws
```
既存のディレクトリを workspace に登録できる。

```shell
    $ ws -<workspaceの頭文字> | --<workspaceのフルネーム>
```
コマンドラインから workspaceに移動できる。

```shell
    $ addws
```
カレントディレクトリを`ws`コマンドに追加できる。

基本的に新しく開発・学習を始めるときは`setDE`コマンドを実行する。これは
- `gitinit`コマンドによるgithub リポジトリの作成と連携
- `mkws`コマンドによる workspace の作成
- `addws`コマンドによる`ws`コマンドの作成

を一括で行う。既存のディレクトリに適用したい場合は各コマンドを個別で実行する。
ちなみに、`ws`コマンドはおまけ。GUI だけでなく CUI で workspace を切り替えたい時に使う。

# 必要な知識
以下の三つはここでは解説しない。
- vscode
- workspaceの使い方
- github

以下は知らなくても実用的には問題ない。簡単な解説を後述。
- shell
- bash
- alias
- githubCLI

### shell
shell（シェル）は、コンピュータを使用すユーザーと実際にコンピュータを動かす仕組みを持っている OS の間をつなぐインターフェースとなるプログラム。ターミナルからコマンドによって命令を与えることができる。

なお、shell と shell スクリプトは異なったもの
- shell : コマンドを OS に渡すためのプログラムそのもの
- シェルスクリプト : shell による命令がまとめられたテキストファイル

続けて実行したい複数のコマンドを、shell スクリプトとしてまとめておけば、そのスクリプトを実行するコマンドを送るだけでいくつかの処理を一気に実行することが可能になる。shell スクリプトの拡張子は`.sh`

### bash
bash は OS の一種である UNIX における shell の一つで、UNIX やそこから派生してできた OS に対し命令文を記述したり shell スプリクトを組むときに使用する。 
UNIX 系統の OS はたとえば macOS, Linux などがある。

### alias
shell コマンドの一種で、コマンドの別名を定義できる。コマンドにおけるショートカットのようなもの。

### githubCLI
githubのさまざまなコマンドが提供されているパッケージ。

インストール手順は[公式のインストール手順](https://github.com/cli/cli/blob/trunk/docs/install_linux.md)を参考に。
```shell
type -p curl >/dev/null || sudo apt install curl -y
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
```

インストールできたことを確認
```shell
gh --version
```

# 実装
以下のような手順で実装を行なっていく。
1. `~/bin`の作成
1. `~/bin/gitinit.sh`の作成
1. `~/bin/getPathFromHome.sh`の作成
1. `~/bin/mkws.sh`の作成
1. `~/bin/ws.sh`の作成
1. `~/bin/addws.sh`の作成
1. `~/bin/setDE.sh`の作成
1. 実行権限の付与
1. alias の登録

### 1. `~/bin`の作成
ホームディレクトリの直下に`bin`ディレクトリを作成。基本的に自動化 shell コマンドはここに置いていく。
```
    $ cd ~
    $ mkdir bin
```

### 2. `~/bin/gitinit.sh`の作成
まず、git リポジトリを自動作成する`gitinit.sh`を作成する。github CLI には対話形式で git リポジトリを作成するコマンド`gh repo create`が提供されているのでほぼそのままだが、README の作成、Private / Public の設定が毎回同じで良ければ、そこも自動化できる。
```shell:~/bin/gitinit.sh
    #!/usr/bin/expect -f
    
    # リポジトリ名の確認
    if {$argc != 1} {
        puts "Write the repository name."
        exit 1
    }
    
    # 引数からリポジトリ名を取得
    set REPO_NAME [lindex $argv 0]
    
    # リポジトリの説明を設定
    set REPO_DESCRIPTION "This is a repository of $REPO_NAME"
    
    # リポジトリの可視性を設定（ここではプライベート）
    set VISIBILITY "Private"
    
    # .gitignore ファイルの設定
    set GITIGNORE "Node"
    
    # ライセンスの設定
    set LICENSE "MIT"
    
    # gh repo create コマンドを実行
    spawn gh repo create $REPO_NAME --description "$REPO_DESCRIPTION" --private --gitignore $GITIGNORE --license $LICENSE
    
    # リポジトリの作成を待つ
    expect eof
    
    # ユーザー名を取得
    set USER_NAME "your username"
    
    # カレントディレクトリにリポジトリをクローン
    spawn git clone https://github.com/$USER_NAME/$REPO_NAME.git
    
    # プロセスの終了を待つ
    expect eof
    
    # READMEファイルを作成
    set readme [open "$REPO_NAME/README.md" "w"]
    puts $readme "# $REPO_NAME\n\n$REPO_DESCRIPTION"
    close $readme
```

### 3. `~/bin/getPathFromHome.sh`の作成
カレントディレクトリのホームディレクトリからの相対パスを取得する`getPathFromHome.sh`を作成する。
```shell:getPathFromHome.sh
    #!/bin/bash
    
    function getPathFromHome() {
        # 現在のディレクトリを取得
        current_dir=$(pwd)
    
        # $HOME ディレクトリを取得
        home_dir="$HOME"
    
        # $HOME ディレクトリから現在のディレクトリまでの相対パスを計算
        relative_path="${current_dir#$home_dir}"
    
        # 結果を表示 (他のファイルで結果だけ使用する際のために、引数を渡すと表示をしないように設定)
        if [ -z "$1" ]; then
            echo "現在のディレクトリから \$HOME ディレクトリまでの相対パス: $relative_path"
        fi
        echo "$relative_path"
}

getPathFromHome $1
```

### 4. `~/bin/mkws.sh`の作成
続いて workspace ファイルを作成する`mkws.sh`を作成する。
```shell:mkws.sh
    #!/bin/bash
    
    function mkws() {
    
        # ワークスペースへのパス
        ws_dir="your path to workspaces"
        file_name=$(basename "$(pwd)")
    
        # ワークスペース名が空でないか確認
        if [ -z "$file_name" ]; then
            echo "Please provide a workspace name."
            return 1
        fi
    
        # ワークスペースがすでに作られていないか確認
        if [ -f "$ws_dir$file_name.code-workspace" ]; then
            echo "The workspace already exists."
            return 1
        fi
    
        RELATIVE_PATH=$(~/bin/getPathFromHome.sh 1)
        echo "$RELATIVE_PATH"
        # .code-workspaceファイルの中身
        workspace_content='{
        "folders": [
            {
                "path": "..'"${RELATIVE_PATH}"'"
            }
        ],
        "settings": {}
        }'
    
        # ワークスペースの作成
        touch "$ws_dir$file_name.code-workspace"
        echo "$workspace_content" > "$ws_dir$file_name.code-workspace"
        echo "The workspace has been created."
        return 0
    }
    
    mkws
```

### 5. `~/bin/ws.sh`の作成
CLI による workspace への移動を行う`ws.sh`を作成する。
```shell:~/bin/ws.sh
    #!/bin/bash
    
    case "$1" in
        -h | --help)
            echo "Usage: _mv_ [directory]"
            echo "Move the current directory to the specified directory."
            return 0
            ;;
    
        --bin)
            code -r ~/yourPathToWorkspace/bin.code-workspace --fullscreen
            ;;
    esac
    
```
    
### 6. `~/bin/addws.sh`の作成
`ws.sh`に新たな workspace を追加する`addws.sh`を作成する。
```shell:~/bin/addws.sh
    #!/bin/bash
    
    function addws() {
            file_name=$(basename "$PWD")
            echo "file_name: $file_name"
            additional_text=
            # 頭文字のみの指定は以下
            # '   -"${file_name:0:1}" | --$file_name)
            '    --$file_name)
                    code -r ~/yourPathToWorkspace/$file_name.code-workspace --fullscreen
                    ;;
            '
    
            echo "additional_text: $additional_text"
            # コマンドがすでに作成されていないか確認
            grep_result=$(grep "$additional_text" ~/bin/ws.sh)
            if [ -z "$grep_result" ]; then
                    echo "The ws command is already added to the specified workspace."
            else
                    sed -i '' -e '$d' ~/bin/ws.sh
                    echo "$additional_text" >> ~/bin/ws.sh
                    sed -i '' -e "\$a\\"$'\n'"esac" ~/bin/ws.sh
            fi
    }
    
    addws
```

### 7. `~/bin/setDE.sh`の作成
開発環境を構築する`setDE.sh`を作成する。
```shell:setDE.sh
    #!/bin/bash
    
    # 実行の確認
    echo "Create a git repository, a new workspace and the ws command. of this directory : $PWD. Enter y to continue."
    read -p "Enter 'y' or 'n': " answer
    
    if [ "$answer" = "y" ]; then
        echo "You chose to continue."
        # Gitレポジトリの初期化
        gitinit.sh "$1"
    
        # ワークスペースの作成
        cd $1
        mkws.sh
    
        # wsコマンドの追加
        addws.sh
    
        echo "The git repository, new workspace and ws command has been created."
    
    elif [ "$answer" = "n" ]; then
        echo "You chose to abort."
    else
        echo "Invalid input. Please enter 'y' or 'n'."
    fi
```

### 8. 実行権限の付与
コードに対し実行権限を与えることによって、実行コマンドを用いることなくファイル名のみでプログラムが実行できるようになる。


ex)
```
    $ bash example.sh -> $ example.sh
    $ python3 example.py -> $ example.py
```

実行権限の付与は`chmod`コマンドで行う。元々`~/bin`を作ってあった人は`*`で一括に権限を付与せずファイル名を指定するのが健全。


```shell
    $ chmod +x ~/bin/*
```

### 9. alias の登録
alias は shell の設定ファイルである`~/.bashrc`や`~/.zshrc`で設定を行う。普段自分が使っているもので設定すること。

```shell:.zshrc
    alias ws='ws.sh'
    alias mkws='mkws.sh'
    alias setDE='setDE.sh'
```

以上8,9のステップで実行が簡略化された。

```
    $ bash setDE.sh -> $ setDE
```
# 終わりに
何かおかしい部分や質問等ありましたらコメントお願いします。