2 Commits
Author SHA1 Message Date
Seungjun Lee a959fdfead add tmux 2026-07-25 22:50:35 +09:00
Seungjun Lee e5ba30d4d1 format code & add todo 2026-07-25 22:50:05 +09:00
3 changed files with 117 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
+1
View File
@@ -100,3 +100,4 @@ Microsoft excel,manual,,no,productivity,Microsoft excel,paid
Microsoft powerpoint,manual,,no,productivity,Microsoft powerpoint,paid Microsoft powerpoint,manual,,no,productivity,Microsoft powerpoint,paid
wget,formula,,yes,utilities,File downloader,free wget,formula,,yes,utilities,File downloader,free
mise,formula,,yes,development,Package version manager,free mise,formula,,yes,development,Package version manager,free
tmux,formula,,yes,development,Terminal multiplexer,free
1 name type mas_id install_on_setup category description price
100 Microsoft powerpoint manual no productivity Microsoft powerpoint paid
101 wget formula yes utilities File downloader free
102 mise formula yes development Package version manager free
103 tmux formula yes development Terminal multiplexer free
+115 -82
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,124 +50,131 @@ 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
raw_list = get_csv() raw_list = get_csv()
temp = {pkg_type:[] for pkg_type in support_types} temp = {pkg_type: [] for pkg_type in support_types}
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()