format code & add todo
This commit is contained in:
@@ -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
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user