add automatic tap detection

This commit is contained in:
2026-07-15 21:58:52 +09:00
parent 585ecab616
commit 7ef0df39f6
2 changed files with 72 additions and 19 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ fastfetch,formula,,yes,utilities,Neofetch alternative,free
mas,formula,,yes,utilities,CLI appstore,free mas,formula,,yes,utilities,CLI appstore,free
tree,formula,,yes,utilities,File structure tree,free tree,formula,,yes,utilities,File structure tree,free
scrcpy,formula,,no,utilities,Android screen mirroring,free scrcpy,formula,,no,utilities,Android screen mirroring,free
thaw,formula,,yes,personalization,Menubar hidder,free thaw,cask,,yes,personalization,Menubar hidder,free
nmap,formula,,no,network,Network scanner,free nmap,formula,,no,network,Network scanner,free
davinci resolve ,manual,,no,creativity,Video editor,free davinci resolve ,manual,,no,creativity,Video editor,free
reef,manual,,no,personalization,"Shortcuts based app switcher reef,manual,,no,personalization,"Shortcuts based app switcher
1 name type mas_id install_on_setup category description price
51 mas formula yes utilities CLI appstore free
52 tree formula yes utilities File structure tree free
53 scrcpy formula no utilities Android screen mirroring free
54 thaw formula cask yes personalization Menubar hidder free
55 nmap formula no network Network scanner free
56 davinci resolve manual no creativity Video editor free
57 reef manual no personalization Shortcuts based app switcher https://github.com/gouwsxander/Reef free
+66 -13
View File
@@ -1,3 +1,10 @@
import csv
import subprocess
import shutil
import datetime
import sys
import re
# ====== Config ====== # ====== Config ======
CSV_FILE = './apps.csv' CSV_FILE = './apps.csv'
BREWFILE = './Brewfile' BREWFILE = './Brewfile'
@@ -11,14 +18,9 @@ MANUAL_TYPE = 'manual'
support_types = list(SUPPORT_CONFIG.keys()) support_types = list(SUPPORT_CONFIG.keys())
def clear():
subprocess.run(['clear'])
import csv
import subprocess
import shutil
import datetime
import sys
clear = lambda: subprocess.run(['clear'])
def check_package(name): def check_package(name):
path = shutil.which(name) path = shutil.which(name)
@@ -35,6 +37,12 @@ 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):
parts = name.split('/')
if len(parts) == 3:
return f'{parts[0]}/{parts[1]}'
return None
def create_brewfile(): def create_brewfile():
try: try:
with open(BREWFILE, 'r') as old: with open(BREWFILE, 'r') as old:
@@ -47,11 +55,15 @@ def create_brewfile():
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()
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'])
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) temp[pkg_type].append(content)
elif pkg_type != MANUAL_TYPE: elif pkg_type != MANUAL_TYPE:
@@ -59,12 +71,50 @@ def create_brewfile():
sys.exit() sys.exit()
with open(BREWFILE, 'w') as f: with open(BREWFILE, 'w') as f:
if taps:
f.write(f'# {"=" * 15} tap {"=" * 15}\n')
for tap in sorted(taps):
f.write(f'tap "{tap}"\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 create Brewfile') print('✅ Successfully created Brewfile')
def handle_untrusted_taps():
taps = set()
try:
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)')
return False
if taps:
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.')
return False
for tap in sorted(taps):
try:
subprocess.run(['brew', 'tap', 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')
return False
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}
@@ -105,6 +155,9 @@ def install_dependencies():
return True return True
def bundle_install(): def bundle_install():
if not handle_untrusted_taps():
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
@@ -125,7 +178,7 @@ def bundle_check():
['brew', 'bundle', 'check', f'--file={BREWFILE}', '--verbose'] ['brew', 'bundle', 'check', f'--file={BREWFILE}', '--verbose']
) )
if result.returncode == 0: if result.returncode == 0:
print('Every 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)')
@@ -139,7 +192,7 @@ def bundle_check():
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 want 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(['brew', 'bundle', 'cleanup', f'--file={BREWFILE}', '--force'], check=True)
print('✅ Successfully cleanup') print('✅ Successfully cleanup')
else: else:
@@ -171,9 +224,9 @@ def select_option():
print() print()
return return
else: else:
print(f'⚠️ Write valid number\n') print('⚠️ Write valid number\n')
except ValueError as e: except ValueError:
print(f'⚠️ Write valid number\n') print('⚠️ Write valid number\n')
def main(): def main():