format code & add todo

This commit is contained in:
Seungjun Lee
2026-07-25 22:50:05 +09:00
parent ab4b05b514
commit e5ba30d4d1
2 changed files with 116 additions and 82 deletions
+1
View File
@@ -2,3 +2,4 @@
Todo: Todo:
☐ Make dependency not hardcoded ☐ Make dependency not hardcoded
☐ Pass depencency detect if package is in list ☐ Pass depencency detect if package is in list
☐ add type in brewfile_tool.py
+113 -80
View File
@@ -1,25 +1,47 @@
import csv import csv
import subprocess
import shutil
import datetime import datetime
import sys
import re import re
import shutil
import subprocess
import sys
# ====== Config ====== # ====== Config ======
CSV_FILE = './apps.csv' CSV_FILE = "./apps.csv"
BREWFILE = './Brewfile' BREWFILE = "./Brewfile"
SUPPORT_CONFIG = { SUPPORT_CONFIG = {
'formula': {'token': 'brew', 'structure': lambda data: f'brew "{data["name"]}"', 'dependency': [] }, "formula": {
'cask' : {'token': 'cask', 'structure': lambda data: f'cask "{data["name"]}"', 'dependency': [] }, "token": "brew",
'mas': {'token': 'mas', 'structure': lambda data: f'mas "{data["name"]}", id: {data["mas_id"]}', 'dependency': [{'name': 'mas', 'script': ['brew', 'install', 'mas']}] }, "structure": lambda data: f'brew "{data["name"]}"',
'vscode': {'token': 'vscode', 'structure': lambda data: f'vscode "{data["name"]}"', 'dependency': [{'name': 'code', 'script': ['brew', 'install', '--cask', 'visual-studio-code']}] } "dependency": [],
},
"cask": {
"token": "cask",
"structure": lambda data: f'cask "{data["name"]}"',
"dependency": [],
},
"mas": {
"token": "mas",
"structure": lambda data: f'mas "{data["name"]}", id: {data["mas_id"]}',
"dependency": [{"name": "mas", "script": ["brew", "install", "mas"]}],
},
"vscode": {
"token": "vscode",
"structure": lambda data: f'vscode "{data["name"]}"',
"dependency": [
{
"name": "code",
"script": ["brew", "install", "--cask", "visual-studio-code"],
} }
MANUAL_TYPE = 'manual' ],
},
}
MANUAL_TYPE = "manual"
support_types = list(SUPPORT_CONFIG.keys()) support_types = list(SUPPORT_CONFIG.keys())
def clear(): def clear():
subprocess.run(['clear']) subprocess.run(["clear"])
def check_package(name): def check_package(name):
@@ -28,28 +50,32 @@ def check_package(name):
return False return False
return True return True
def check_brew(): def check_brew():
if not check_package('brew'): if not check_package("brew"):
print('❌ Unable to find brew') print("❌ Unable to find brew")
sys.exit() sys.exit()
def get_csv(): def get_csv():
with open(CSV_FILE, 'r', encoding='utf-8-sig') as f: with open(CSV_FILE, "r", encoding="utf-8-sig") as f:
return list(csv.DictReader(f)) return list(csv.DictReader(f))
def extract_tap(name): def extract_tap(name):
parts = name.split('/') parts = name.split("/")
if len(parts) == 3: if len(parts) == 3:
return f'{parts[0]}/{parts[1]}' return f"{parts[0]}/{parts[1]}"
return None return None
def create_brewfile(): def create_brewfile():
try: try:
with open(BREWFILE, 'r') as old: with open(BREWFILE, "r") as old:
stamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
with open(f'./Brewfile_{stamp}.backup', 'w') as new: with open(f"./Brewfile_{stamp}.backup", "w") as new:
new.writelines(old.readlines()) new.writelines(old.readlines())
print(f'📢 Backup old Brewfile (Brewfile_{stamp}.backup)') print(f"📢 Backup old Brewfile (Brewfile_{stamp}.backup)")
except FileNotFoundError: except FileNotFoundError:
pass pass
@@ -58,94 +84,97 @@ def create_brewfile():
taps = set() taps = set()
for row in raw_list: for row in raw_list:
pkg_type = row['type'] pkg_type = row["type"]
if pkg_type in temp.keys(): if pkg_type in temp.keys():
if row['install_on_setup'] == 'yes': if row["install_on_setup"] == "yes":
tap = extract_tap(row['name']) tap = extract_tap(row["name"])
if tap: if tap:
taps.add(tap) taps.add(tap)
content = f'# {row["description"]}\n{SUPPORT_CONFIG[pkg_type]["structure"](row)}\n' content = f"# {row['description']}\n{SUPPORT_CONFIG[pkg_type]['structure'](row)}\n"
temp[pkg_type].append(content) temp[pkg_type].append(content)
elif pkg_type != MANUAL_TYPE: elif pkg_type != MANUAL_TYPE:
print(f'Error: {row["name"]} {pkg_type}') print(f"Error: {row['name']} {pkg_type}")
sys.exit() sys.exit()
with open(BREWFILE, 'w') as f: with open(BREWFILE, "w") as f:
if taps: if taps:
f.write(f'# {"=" * 15} tap {"=" * 15}\n') f.write(f"# {'=' * 15} tap {'=' * 15}\n")
for tap in sorted(taps): for tap in sorted(taps):
f.write(f'tap "{tap}"\n') f.write(f'tap "{tap}"\n')
f.write('\n') f.write("\n")
for pkg_type in support_types: for pkg_type in support_types:
f.write(f'# {"=" * 15} {pkg_type} {"=" * 15}\n') f.write(f"# {'=' * 15} {pkg_type} {'=' * 15}\n")
f.writelines(temp[pkg_type]) f.writelines(temp[pkg_type])
f.write('\n') f.write("\n")
print("✅ Successfully created Brewfile")
print('✅ Successfully created Brewfile')
def handle_untrusted_taps(): def handle_untrusted_taps():
taps = set() taps = set()
try: try:
with open(BREWFILE, 'r') as f: with open(BREWFILE, "r") as f:
for line in f: for line in f:
m = re.fullmatch(r'tap "([^/]+/[^/]+)"', line.strip()) m = re.fullmatch(r'tap "([^/]+/[^/]+)"', line.strip())
if m: if m:
taps.add(m.group(1)) taps.add(m.group(1))
except FileNotFoundError: except FileNotFoundError:
print('❌ Unable to find Brewfile (create it first)') print("❌ Unable to find Brewfile (create it first)")
return False return False
if taps: if taps:
print('⚠️ These third-party taps are needed') print("⚠️ These third-party taps are needed")
for tap in sorted(taps): for tap in sorted(taps):
print(f' - {tap}') print(f" - {tap}")
print(' These taps is not official and my execute certain script.\n') print(" These taps is not official and my execute certain script.\n")
if input('Trust these taps? (y/N): ').strip().lower() !='y': if input("Trust these taps? (y/N): ").strip().lower() != "y":
print('📢 Operation is canceled as these taps are not allowed.') print("📢 Operation is canceled as these taps are not allowed.")
return False return False
for tap in sorted(taps): for tap in sorted(taps):
try: try:
subprocess.run(['brew', 'tap', tap], check=True) subprocess.run(["brew", "tap", tap], check=True)
subprocess.run(['brew', 'trust', tap], check=True) subprocess.run(["brew", "trust", tap], check=True)
print(f'✅ Successfully installed tap "{tap}"') print(f'✅ Successfully installed tap "{tap}"')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f'❌ Failed to install tap "{tap}" ({e.returncode})') print(f'❌ Failed to install tap "{tap}" ({e.returncode})')
return False return False
except FileNotFoundError: except FileNotFoundError:
print('❌ Unable to find brew') print("❌ Unable to find brew")
return False return False
return True return True
def get_brewfile_types(): def get_brewfile_types():
token_to_type = {SUPPORT_CONFIG[t]['token']: t for t in support_types} token_to_type = {SUPPORT_CONFIG[t]["token"]: t for t in support_types}
types = set() types = set()
try: try:
with open(BREWFILE, 'r') as f: with open(BREWFILE, "r") as f:
for line in f: for line in f:
token = line.strip().split(' ', 1)[0] token = line.strip().split(" ", 1)[0]
if token in token_to_type: if token in token_to_type:
types.add(token_to_type[token]) types.add(token_to_type[token])
except FileNotFoundError: except FileNotFoundError:
print('❌ Unable to find Brewfile (create it first)') print("❌ Unable to find Brewfile (create it first)")
return types return types
def install_dependencies(): def install_dependencies():
dependencies = [] dependencies = []
seen = set() seen = set()
for pkg_type in get_brewfile_types(): for pkg_type in get_brewfile_types():
for dep in SUPPORT_CONFIG[pkg_type]['dependency']: for dep in SUPPORT_CONFIG[pkg_type]["dependency"]:
if dep['name'] not in seen: if dep["name"] not in seen:
seen.add(dep['name']) seen.add(dep["name"])
dependencies.append(dep) dependencies.append(dep)
for dep in dependencies: for dep in dependencies:
if check_package(dep['name']): if check_package(dep["name"]):
print(f'✅ Dependency "{dep["name"]}" already installed') print(f'✅ Dependency "{dep["name"]}" already installed')
continue continue
print(f'📢 Installing missing dependency "{dep["name"]}"...') print(f'📢 Installing missing dependency "{dep["name"]}"...')
try: try:
subprocess.run(dep['script'], check=True) subprocess.run(dep["script"], check=True)
print(f'✅ Successfully installed dependency "{dep["name"]}"') print(f'✅ Successfully installed dependency "{dep["name"]}"')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f'❌ Failed to install dependency "{dep["name"]}" ({e.returncode})') print(f'❌ Failed to install dependency "{dep["name"]}" ({e.returncode})')
@@ -155,79 +184,82 @@ def install_dependencies():
return False return False
return True return True
def bundle_install(): def bundle_install():
if not handle_untrusted_taps(): if not handle_untrusted_taps():
return return
if not install_dependencies(): if not install_dependencies():
print('❌ Aborting install due to missing dependency') print("❌ Aborting install due to missing dependency")
return return
try: try:
subprocess.run(['brew', 'bundle', 'install', f'--file={BREWFILE}'], check=True) subprocess.run(["brew", "bundle", "install", f"--file={BREWFILE}"], check=True)
print('✅ Successfully install all packages') print("✅ Successfully install all packages")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f'❌ Failed to install some packages ({e.returncode})') print(f"❌ Failed to install some packages ({e.returncode})")
except FileNotFoundError: except FileNotFoundError:
print('❌ Unable to find brew') print("❌ Unable to find brew")
def bundle_check(): def bundle_check():
try: try:
print('📢 Check script running') print("📢 Check script running")
result = subprocess.run( result = subprocess.run(
['brew', 'bundle', 'check', f'--file={BREWFILE}', '--verbose'] ["brew", "bundle", "check", f"--file={BREWFILE}", "--verbose"]
) )
if result.returncode == 0: if result.returncode == 0:
print('✅ All packages are installed') print("✅ All packages are installed")
return True return True
else: else:
print('⚠️ There are uninstalled package(s)') print("⚠️ There are uninstalled package(s)")
return False return False
except FileNotFoundError: except FileNotFoundError:
print('❌ Unable to find brew') print("❌ Unable to find brew")
return False return False
def bundle_cleanup(): def bundle_cleanup():
try: try:
subprocess.run(['brew', 'bundle', 'cleanup', f'--file={BREWFILE}']) subprocess.run(["brew", "bundle", "cleanup", f"--file={BREWFILE}"])
if input('⚠️ Are sure to remove? (y/N): ').strip().lower() == 'y': if input("⚠️ Are sure to remove? (y/N): ").strip().lower() == "y":
subprocess.run(['brew', 'bundle', 'cleanup', f'--file={BREWFILE}', '--force'], check=True) subprocess.run(
print('✅ Successfully cleanup') ["brew", "bundle", "cleanup", f"--file={BREWFILE}", "--force"],
check=True,
)
print("✅ Successfully cleanup")
else: else:
print('📢 Canceled') print("📢 Canceled")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f'❌ Failed to cleanup ({e.returncode})') print(f"❌ Failed to cleanup ({e.returncode})")
except FileNotFoundError: except FileNotFoundError:
print('❌ Unable to find brew') print("❌ Unable to find brew")
def select_option(): def select_option():
options = [ options = [
{'text': 'Create Brewfile', 'func': create_brewfile}, {"text": "Create Brewfile", "func": create_brewfile},
{'text': 'Install packages', 'func': bundle_install}, {"text": "Install packages", "func": bundle_install},
{'text': 'Check packages', 'func': bundle_check}, {"text": "Check packages", "func": bundle_check},
{'text': 'Cleanup packages', 'func': bundle_cleanup}, {"text": "Cleanup packages", "func": bundle_cleanup},
{'text': 'Exit', 'func': sys.exit} {"text": "Exit", "func": sys.exit},
] ]
while True: while True:
print('Select option') print("Select option")
for idx, row in enumerate(options, 1): for idx, row in enumerate(options, 1):
print(f' {idx}) {row["text"]}') print(f" {idx}) {row['text']}")
raw = input('option: ').strip() raw = input("option: ").strip()
try: try:
clear() clear()
idx = int(raw) - 1 idx = int(raw) - 1
if 0 <= idx < len(options): if 0 <= idx < len(options):
options[idx]['func']() options[idx]["func"]()
print() print()
return return
else: else:
print('⚠️ Write valid number\n') print("⚠️ Write valid number\n")
except ValueError: except ValueError:
print('⚠️ Write valid number\n') print("⚠️ Write valid number\n")
def main(): def main():
@@ -235,5 +267,6 @@ def main():
while True: while True:
select_option() select_option()
if __name__ == '__main__':
if __name__ == "__main__":
main() main()