9 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
Seungjun Lee ab4b05b514 add mise 2026-07-20 10:15:31 +09:00
Seungjun Lee 2a3ea43e36 add vscode extension dependencies 2026-07-19 09:17:53 +09:00
Seungjun Lee d08a47d63c remove busycontacts 2026-07-19 08:59:06 +09:00
Seungjun Lee cd93fdc242 add wget 2026-07-18 13:00:04 +09:00
Seungjun Lee 7c75ee9640 add microsoft things 2026-07-18 11:17:11 +09:00
Seungjun Lee 04fef5ab44 add rcmd, droppy 2026-07-16 20:16:00 +09:00
Seungjun Lee 9acc794a6f remove alcove & edit runcat in apps.csv 2026-07-16 11:20:00 +09:00
3 changed files with 219 additions and 176 deletions
+1
View File
@@ -2,3 +2,4 @@
Todo:
☐ Make dependency not hardcoded
☐ Pass depencency detect if package is in list
☐ add type in brewfile_tool.py
+16 -7
View File
@@ -1,4 +1,5 @@
name,type,mas_id,install_on_setup,category,description,price
rcmd,cask,,yes,personalization,Keyboard based app switcher,paid
ente-auth,cask,,yes,security,Free secure two-factor auth service,free
android-platform-tools,cask,,no,utilities,ADB tools,free
sozercan/repo/kaset,cask,,yes,media,MacOS YTM client,free
@@ -27,7 +28,6 @@ rectangle,cask,,yes,personalization,Window management tool,free
karabiner-elements,cask,,yes,personalization,Keyboard customization tool,free
zotero,cask,,no,knowledge,Reference organize tool,free
affine,cask,,yes,knowledge,Opensource notion alternative,free
busycontacts,cask,,yes,productivity,Contact management tool with CalDAV support,paid
affinity,cask,,yes,creativity,Photoshop alternative by canva,free
discord,cask,,yes,social,Discord desktop client,free
shifty,cask,,yes,personalization,Night shift adjust tool,free
@@ -35,7 +35,6 @@ keka,cask,,yes,utilities,File extraction enhancer,free
keyboardcleantool,cask,,yes,utilities,Lock keyboard input during cleaning,free
appcleaner,cask,,yes,utilities,Remove app without residue,free
monitorcontrol,cask,,yes,personalization,Control external monitor brightness,free
alcove,cask,,yes,personalization,Notch app,paid
tunnelbear,cask,,yes,network,VPN,free
arduino-ide,cask,,no,development,Arduino IDE,free
clop,cask,,yes,personalization,File compressor,free
@@ -45,7 +44,7 @@ openmtp,cask,,yes,utilities,Android device connector,free
responsively,cask,,no,development,Browser for responsible website development,free
telegram,cask,,yes,social,Telegram desktop client,free
wireshark-app,cask,,no,network,Network packets capturer,free
git,formula,,yes,development,Version management tool,free
git,formula,,yes,development,File version manager,free
mole,formula,,yes,utilities,System optimizer,free
fastfetch,formula,,yes,utilities,Neofetch alternative,free
mas,formula,,yes,utilities,CLI appstore,free
@@ -54,8 +53,6 @@ scrcpy,formula,,no,utilities,Android screen mirroring,free
thaw,cask,,yes,personalization,Menubar hidder,free
nmap,formula,,no,network,Network scanner,free
davinci resolve ,manual,,no,creativity,Video editor,free
reef,manual,,no,personalization,"Shortcuts based app switcher
https://github.com/gouwsxander/Reef",free
tageditor,mas,984278082,no,utilities,Audio file metadata editor,free
Allkdic,mas,1033453958,yes,productivity,Menubar dictionary,free
Bitwarden,mas,1352778147,yes,security,Password manager,free
@@ -64,7 +61,7 @@ KakaoTalk,mas,869223134,yes,social,KakaoTalk,free
Linearity Curve,mas,1219074514,no,creativity,Vector based design tool,free
Poolsuite FM,mas,1514817810,no,media,Internet music radio,free
Print to PDF,mas,1639234272,yes,utilities,Save pdf to file,free
RunCat,mas,1429033973,yes,personalization,Menubar system usage visualizer,free
RunCat Neo,mas,6757801838,yes,personalization,Menubar system usage visualizer,free
Texty,mas,1538996043,yes,social,Third-party google chat desktop client,free
Windows App,mas,1295203466,yes,productivity,Windows RDP client,free
WireGuard,mas,1451685025,yes,network,Wireguard client,free
@@ -80,9 +77,13 @@ marp-team.marp-vscode,vscode,,no,integration,Support marp(MD based presentation)
matt-meyers.vscode-dbml,vscode,,yes,integration,Support DBML,free
mechatroner.rainbow-csv,vscode,,yes,format,CSV highlighter,free
ms-azuretools.vscode-containers,vscode,,yes,tool,Container tool,free
ms-python.black-formatter,vscode,,yes,format,Python formatter,free
ms-python.python,vscode,,yes,language,Python integration,free
ms-python.debugpy,vscode,,yes,language,Dependency of ms-python.python,free
ms-python.vscode-pylance,vscode,,yes,language,Dependency of ms-python.python,free
ms-python.vscode-python-envs,vscode,,yes,language,Dependency of ms-python.python,free
ms-vscode-remote.remote-ssh,vscode,,yes,tool,SSH conntection,free
ms-vscode-remote.remote-ssh-edit,vscode,,yes,tool,Dependency of ms-vscode-remote.remote-ssh,free
ms-vscode.remote-explorer,vscode,,yes,tool,Dependency of ms-vscode-remote.remote-ssh,free
pkief.material-icon-theme,vscode,,yes,theme,Best icon,free
pkief.material-product-icons,vscode,,yes,theme,Best product icon,free
pomdtr.excalidraw-editor,vscode,,no,integration,Excalidraw integration,free
@@ -92,3 +93,11 @@ svelte.svelte-vscode,vscode,,yes,language,Sevelte integration,free
wayou.vscode-todo-highlight,vscode,,yes,tool,Todo file highlighter,free
yzhang.markdown-all-in-one,vscode,,yes,language,Markdown highlighter & formatter,free
zhuangtongfa.material-theme,vscode,,yes,theme,Best darkmode theme (One Dark Pro),free
charliermarsh.ruff,vscode,,yes,format,Python formatter,free
droppy,manual,,no,personalization,Notch customization,paid
Microsoft word,manual,,no,productivity,Microsoft word,paid
Microsoft excel,manual,,no,productivity,Microsoft excel,paid
Microsoft powerpoint,manual,,no,productivity,Microsoft powerpoint,paid
wget,formula,,yes,utilities,File downloader,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
2 rcmd cask yes personalization Keyboard based app switcher paid
3 ente-auth cask yes security Free secure two-factor auth service free
4 android-platform-tools cask no utilities ADB tools free
5 sozercan/repo/kaset cask yes media MacOS YTM client free
28 karabiner-elements cask yes personalization Keyboard customization tool free
29 zotero cask no knowledge Reference organize tool free
30 affine cask yes knowledge Opensource notion alternative free
busycontacts cask yes productivity Contact management tool with CalDAV support paid
31 affinity cask yes creativity Photoshop alternative by canva free
32 discord cask yes social Discord desktop client free
33 shifty cask yes personalization Night shift adjust tool free
35 keyboardcleantool cask yes utilities Lock keyboard input during cleaning free
36 appcleaner cask yes utilities Remove app without residue free
37 monitorcontrol cask yes personalization Control external monitor brightness free
alcove cask yes personalization Notch app paid
38 tunnelbear cask yes network VPN free
39 arduino-ide cask no development Arduino IDE free
40 clop cask yes personalization File compressor free
44 responsively cask no development Browser for responsible website development free
45 telegram cask yes social Telegram desktop client free
46 wireshark-app cask no network Network packets capturer free
47 git formula yes development Version management tool File version manager free
48 mole formula yes utilities System optimizer free
49 fastfetch formula yes utilities Neofetch alternative free
50 mas formula yes utilities CLI appstore free
53 thaw cask yes personalization Menubar hidder free
54 nmap formula no network Network scanner free
55 davinci resolve manual no creativity Video editor free
reef manual no personalization Shortcuts based app switcher https://github.com/gouwsxander/Reef free
tageditor mas 984278082 no utilities Audio file metadata editor free
56 Allkdic tageditor mas 1033453958 984278082 yes no productivity utilities Menubar dictionary Audio file metadata editor free
57 Bitwarden Allkdic mas 1352778147 1033453958 yes security productivity Password manager Menubar dictionary free
58 Hancom Office HWP Viewer Bitwarden mas 416746898 1352778147 yes productivity security Hancom Office HWP Viewer Password manager free
61 Poolsuite FM Linearity Curve mas 1514817810 1219074514 no media creativity Internet music radio Vector based design tool free
62 Print to PDF Poolsuite FM mas 1639234272 1514817810 yes no utilities media Save pdf to file Internet music radio free
63 RunCat Print to PDF mas 1429033973 1639234272 yes personalization utilities Menubar system usage visualizer Save pdf to file free
64 Texty RunCat Neo mas 1538996043 6757801838 yes social personalization Third-party google chat desktop client Menubar system usage visualizer free
65 Windows App Texty mas 1295203466 1538996043 yes productivity social Windows RDP client Third-party google chat desktop client free
66 WireGuard Windows App mas 1451685025 1295203466 yes network productivity Wireguard client Windows RDP client free
67 Xcode WireGuard mas 497799835 1451685025 no yes development network Xcode Wireguard client free
77 mechatroner.rainbow-csv matt-meyers.vscode-dbml vscode yes format integration CSV highlighter Support DBML free
78 ms-azuretools.vscode-containers mechatroner.rainbow-csv vscode yes tool format Container tool CSV highlighter free
79 ms-python.black-formatter ms-azuretools.vscode-containers vscode yes format tool Python formatter Container tool free
ms-python.python vscode yes language Python integration free
80 ms-vscode-remote.remote-ssh ms-python.python vscode yes tool language SSH conntection Python integration free
81 ms-python.debugpy vscode yes language Dependency of ms-python.python free
82 ms-python.vscode-pylance vscode yes language Dependency of ms-python.python free
83 ms-python.vscode-python-envs vscode yes language Dependency of ms-python.python free
84 pkief.material-icon-theme ms-vscode-remote.remote-ssh vscode yes theme tool Best icon SSH conntection free
85 ms-vscode-remote.remote-ssh-edit vscode yes tool Dependency of ms-vscode-remote.remote-ssh free
86 ms-vscode.remote-explorer vscode yes tool Dependency of ms-vscode-remote.remote-ssh free
87 pkief.material-product-icons pkief.material-icon-theme vscode yes theme Best product icon Best icon free
88 pomdtr.excalidraw-editor pkief.material-product-icons vscode no yes integration theme Excalidraw integration Best product icon free
89 rickaym.manim-sideview pomdtr.excalidraw-editor vscode no integration Manim integration Excalidraw integration free
93 yzhang.markdown-all-in-one wayou.vscode-todo-highlight vscode yes language tool Markdown highlighter & formatter Todo file highlighter free
94 zhuangtongfa.material-theme yzhang.markdown-all-in-one vscode yes theme language Best darkmode theme (One Dark Pro) Markdown highlighter & formatter free
95 zhuangtongfa.material-theme vscode yes theme Best darkmode theme (One Dark Pro) free
96 charliermarsh.ruff vscode yes format Python formatter free
97 droppy manual no personalization Notch customization paid
98 Microsoft word manual no productivity Microsoft word paid
99 Microsoft excel manual no productivity Microsoft excel paid
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
+114 -81
View File
@@ -1,25 +1,47 @@
import csv
import subprocess
import shutil
import datetime
import sys
import re
import shutil
import subprocess
import sys
# ====== Config ======
CSV_FILE = './apps.csv'
BREWFILE = './Brewfile'
CSV_FILE = "./apps.csv"
BREWFILE = "./Brewfile"
SUPPORT_CONFIG = {
'formula': {'token': 'brew', 'structure': lambda data: f'brew "{data["name"]}"', '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']}] }
"formula": {
"token": "brew",
"structure": lambda data: f'brew "{data["name"]}"',
"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())
def clear():
subprocess.run(['clear'])
subprocess.run(["clear"])
def check_package(name):
@@ -28,124 +50,131 @@ def check_package(name):
return False
return True
def check_brew():
if not check_package('brew'):
print('❌ Unable to find brew')
if not check_package("brew"):
print("❌ Unable to find brew")
sys.exit()
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))
def extract_tap(name):
parts = name.split('/')
parts = name.split("/")
if len(parts) == 3:
return f'{parts[0]}/{parts[1]}'
return f"{parts[0]}/{parts[1]}"
return None
def create_brewfile():
try:
with open(BREWFILE, 'r') as old:
stamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
with open(f'./Brewfile_{stamp}.backup', 'w') as new:
with open(BREWFILE, "r") as old:
stamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
with open(f"./Brewfile_{stamp}.backup", "w") as new:
new.writelines(old.readlines())
print(f'📢 Backup old Brewfile (Brewfile_{stamp}.backup)')
print(f"📢 Backup old Brewfile (Brewfile_{stamp}.backup)")
except FileNotFoundError:
pass
raw_list = get_csv()
temp = {pkg_type:[] for pkg_type in support_types}
temp = {pkg_type: [] for pkg_type in support_types}
taps = set()
for row in raw_list:
pkg_type = row['type']
pkg_type = row["type"]
if pkg_type in temp.keys():
if row['install_on_setup'] == 'yes':
tap = extract_tap(row['name'])
if row["install_on_setup"] == "yes":
tap = extract_tap(row["name"])
if 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)
elif pkg_type != MANUAL_TYPE:
print(f'Error: {row["name"]} {pkg_type}')
print(f"Error: {row['name']} {pkg_type}")
sys.exit()
with open(BREWFILE, 'w') as f:
with open(BREWFILE, "w") as f:
if taps:
f.write(f'# {"=" * 15} tap {"=" * 15}\n')
f.write(f"# {'=' * 15} tap {'=' * 15}\n")
for tap in sorted(taps):
f.write(f'tap "{tap}"\n')
f.write('\n')
f.write("\n")
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.write('\n')
f.write("\n")
print("✅ Successfully created Brewfile")
print('✅ Successfully created Brewfile')
def handle_untrusted_taps():
taps = set()
try:
with open(BREWFILE, 'r') as f:
with open(BREWFILE, "r") as f:
for line in f:
m = re.fullmatch(r'tap "([^/]+/[^/]+)"', line.strip())
if m:
taps.add(m.group(1))
except FileNotFoundError:
print('❌ Unable to find Brewfile (create it first)')
print("❌ Unable to find Brewfile (create it first)")
return False
if taps:
print('⚠️ These third-party taps are needed')
print("⚠️ These third-party taps are needed")
for tap in sorted(taps):
print(f' - {tap}')
print(' These taps is not official and my execute certain script.\n')
if input('Trust these taps? (y/N): ').strip().lower() !='y':
print('📢 Operation is canceled as these taps are not allowed.')
print(f" - {tap}")
print(" These taps is not official and my execute certain script.\n")
if input("Trust these taps? (y/N): ").strip().lower() != "y":
print("📢 Operation is canceled as these taps are not allowed.")
return False
for tap in sorted(taps):
try:
subprocess.run(['brew', 'tap', tap], check=True)
subprocess.run(['brew', 'trust', tap], check=True)
subprocess.run(["brew", "tap", tap], check=True)
subprocess.run(["brew", "trust", tap], check=True)
print(f'✅ Successfully installed tap "{tap}"')
except subprocess.CalledProcessError as e:
print(f'❌ Failed to install tap "{tap}" ({e.returncode})')
return False
except FileNotFoundError:
print('❌ Unable to find brew')
print("❌ Unable to find brew")
return False
return True
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()
try:
with open(BREWFILE, 'r') as f:
with open(BREWFILE, "r") as f:
for line in f:
token = line.strip().split(' ', 1)[0]
token = line.strip().split(" ", 1)[0]
if token in token_to_type:
types.add(token_to_type[token])
except FileNotFoundError:
print('❌ Unable to find Brewfile (create it first)')
print("❌ Unable to find Brewfile (create it first)")
return types
def install_dependencies():
dependencies = []
seen = set()
for pkg_type in get_brewfile_types():
for dep in SUPPORT_CONFIG[pkg_type]['dependency']:
if dep['name'] not in seen:
seen.add(dep['name'])
for dep in SUPPORT_CONFIG[pkg_type]["dependency"]:
if dep["name"] not in seen:
seen.add(dep["name"])
dependencies.append(dep)
for dep in dependencies:
if check_package(dep['name']):
if check_package(dep["name"]):
print(f'✅ Dependency "{dep["name"]}" already installed')
continue
print(f'📢 Installing missing dependency "{dep["name"]}"...')
try:
subprocess.run(dep['script'], check=True)
subprocess.run(dep["script"], check=True)
print(f'✅ Successfully installed dependency "{dep["name"]}"')
except subprocess.CalledProcessError as e:
print(f'❌ Failed to install dependency "{dep["name"]}" ({e.returncode})')
@@ -155,79 +184,82 @@ def install_dependencies():
return False
return True
def bundle_install():
if not handle_untrusted_taps():
return
if not install_dependencies():
print('❌ Aborting install due to missing dependency')
print("❌ Aborting install due to missing dependency")
return
try:
subprocess.run(['brew', 'bundle', 'install', f'--file={BREWFILE}'], check=True)
print('✅ Successfully install all packages')
subprocess.run(["brew", "bundle", "install", f"--file={BREWFILE}"], check=True)
print("✅ Successfully install all packages")
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:
print('❌ Unable to find brew')
print("❌ Unable to find brew")
def bundle_check():
try:
print('📢 Check script running')
print("📢 Check script running")
result = subprocess.run(
['brew', 'bundle', 'check', f'--file={BREWFILE}', '--verbose']
["brew", "bundle", "check", f"--file={BREWFILE}", "--verbose"]
)
if result.returncode == 0:
print('✅ All packages are installed')
print("✅ All packages are installed")
return True
else:
print('⚠️ There are uninstalled package(s)')
print("⚠️ There are uninstalled package(s)")
return False
except FileNotFoundError:
print('❌ Unable to find brew')
print("❌ Unable to find brew")
return False
def bundle_cleanup():
try:
subprocess.run(['brew', 'bundle', 'cleanup', f'--file={BREWFILE}'])
if input('⚠️ Are sure to remove? (y/N): ').strip().lower() == 'y':
subprocess.run(['brew', 'bundle', 'cleanup', f'--file={BREWFILE}', '--force'], check=True)
print('✅ Successfully cleanup')
subprocess.run(["brew", "bundle", "cleanup", f"--file={BREWFILE}"])
if input("⚠️ Are sure to remove? (y/N): ").strip().lower() == "y":
subprocess.run(
["brew", "bundle", "cleanup", f"--file={BREWFILE}", "--force"],
check=True,
)
print("✅ Successfully cleanup")
else:
print('📢 Canceled')
print("📢 Canceled")
except subprocess.CalledProcessError as e:
print(f'❌ Failed to cleanup ({e.returncode})')
print(f"❌ Failed to cleanup ({e.returncode})")
except FileNotFoundError:
print('❌ Unable to find brew')
print("❌ Unable to find brew")
def select_option():
options = [
{'text': 'Create Brewfile', 'func': create_brewfile},
{'text': 'Install packages', 'func': bundle_install},
{'text': 'Check packages', 'func': bundle_check},
{'text': 'Cleanup packages', 'func': bundle_cleanup},
{'text': 'Exit', 'func': sys.exit}
{"text": "Create Brewfile", "func": create_brewfile},
{"text": "Install packages", "func": bundle_install},
{"text": "Check packages", "func": bundle_check},
{"text": "Cleanup packages", "func": bundle_cleanup},
{"text": "Exit", "func": sys.exit},
]
while True:
print('Select option')
print("Select option")
for idx, row in enumerate(options, 1):
print(f' {idx}) {row["text"]}')
raw = input('option: ').strip()
print(f" {idx}) {row['text']}")
raw = input("option: ").strip()
try:
clear()
idx = int(raw) - 1
if 0 <= idx < len(options):
options[idx]['func']()
options[idx]["func"]()
print()
return
else:
print('⚠️ Write valid number\n')
print("⚠️ Write valid number\n")
except ValueError:
print('⚠️ Write valid number\n')
print("⚠️ Write valid number\n")
def main():
@@ -235,5 +267,6 @@ def main():
while True:
select_option()
if __name__ == '__main__':
if __name__ == "__main__":
main()