🍎

開発ディレクトリ構成 ベストプラクティス

2024/06/24に公開1

はじめに

多くの開発者がGitHubを使ってプロジェクトのバージョン管理を行っています。GitHubからリポジトリをクローンして、ローカルマシンで開発を進めるのが一般的な流れでしょう。しかし、いざクローンするとなると、どのディレクトリにクローンするか迷うことがありませんか?いい加減なディレクトリにクローンすると、後でリポジトリを見失って、開発生産性が低下することもあるでしょう。「あれ、どこにクローンしたっけ?」と頭を抱えた経験、ありませんか?私はあります。そんなわけで、今一度ディレクトリ構成を見直す必要があると思いました。

本記事では、ローカルマシンの開発ディレクトリ構成について、私が考えるベストプラクティスを紹介します。ディレクトリ迷子から卒業しましょう!

ベストプラクティス

  • /Users/$USER/dev/github を作成
  • GitHubからクローンしたリポジトリは、/Users/$USER/dev/github/<github user name>/<repository name> に配置する
    • 要は、/Users/$USER/dev/github/<github user name> にて、git clone <repository url>を実行する

以上である。ポイントとしては、Github上のディレクトリ構成とローカルマシン上のディレクトリ構成を一致させていることである。こうすることで、GitHub上のリポジトリとローカルマシンにクローンしたリポジトリを一対一で対応させることができ、リポジトリの管理が容易になる。

自分の場合、以下のようなディレクトリ構成になっている。

# GitHub管理ディレクトリ
pwd
/Users/kota/dev/github

# GitHubユーザ群
ls
drwxr-xr-x   4 kota  staff   128 Jun 19 22:31 aws
drwxr-xr-x  78 kota  staff  2496 Jun  1 23:22 yagikota

# リポジトリ群
ls -l aws
total 0
drwxr-xr-x  30 kota  staff  960 Jun 19 23:30 aws-sdk-go-v2

ディレクトリ再構築スクリプト

ただ、上記のディレクトリ構成を手動で作成するのは面倒であるので、スクリプトを作成して自動でディレクトリを再構築ることをおすすめする。

  1. /Users/$USER/dev/にGitHubからクローンしたリポジトリを全て配置(これは手動😭)
  2. /Users/$USER/dev/restruct.sh を作成し、python3 restruct.pyを実行
restruct.sh
import os
import shutil
import subprocess

def is_github_repo(directory):
    # Check if the directory is a git repository
    git_dir = os.path.join(directory, ".git")
    if not os.path.exists(git_dir):
        return False

    # Get the remote origin URL
    try:
        result = subprocess.run(
            ["git", "-C", directory, "config", "--get", "remote.origin.url"],
            capture_output=True,
            text=True,
            check=True,
        )
        remote_url = result.stdout.strip()
        # Check if the URL is from GitHub
        return remote_url.startswith("https://github.com/") or remote_url.startswith(
            "git@github.com:"
        )
    except subprocess.CalledProcessError:
        return False


def move_directories(base_dir_path, src_dir_name='github'):
    for item in os.listdir(base_dir_path):
        item_path = os.path.join(base_dir_path, item)
        if os.path.isdir(item_path) and is_github_repo(item_path):
            result = subprocess.run(
                ["git", "-C", item_path, "config", "--get", "remote.origin.url"],
                capture_output=True,
                text=True,
                check=True,
            )
            remote_url = result.stdout.strip()
            new_path = os.path.join(
                base_dir_path,
                src_dir_name,
                remote_url.replace("https://github.com/", "")
                .replace("git@github.com:", "")
                .replace(".git", ""),
            )

            if os.path.exists(new_path):
                print(f"Directory {new_path} already exists")
                continue

            os.makedirs(os.path.dirname(new_path), exist_ok=True)
            shutil.move(item_path, new_path)
            print(f"Moved {item_path} to {new_path}")


def remove_empty_directories(base_dir):
    for item in os.listdir(base_dir):
        item_path = os.path.join(base_dir, item)
        if os.path.isdir(item_path):
            items = os.listdir(item_path)
            if len(items) == 0:
                os.rmdir(item_path)
                print(f"Removed empty directory {item_path}")
            else:
                continue

# Set the base directory to the current directory
curr_dir = os.getcwd()
move_directories(curr_dir)
remove_empty_directories(curr_dir)

実行前
実行前

実行後
実行後

スクリプトを実行すると、図のようにディレクトリが再構築される。図の場合だと、aws-sdk-go-v2のクローンが/Users/kota/dev/github/aws/aws-sdk-go-v2に自動で移動されている。

おわりに

開発ディレクトリの構成は、一見些細なことのように思えますが、実際には開発生産性に大きな影響を与えます。美しいディレクトリ構成を採用することで、リポジトリの管理が容易になり、開発自体もよりスムーズに進めることができると思います。この記事で紹介したベストプラクティスを参考に、ぜひあなたの開発ディレクトリ構成を見直してみてください。また、他にも良いディレクトリ構成があれば、ぜひ教えていただきたいです。

元ネタ

元ネタは、MetaMaskの方の「My Development Directory Structure」という記事です。

Xでもご本人が

completely mirroring GitHub structure is the easiest to manage about and I always know where x repo is

と言っていました。

Money Forward Developers

Discussion