import os
import platform
import shutil
import subprocess
import sys
import tarfile
import urllib.request
import zipfile
RELEASE_URL = "https://github.com/fastfetch-cli/fastfetch/releases/latest/download/{asset}"
INSTALL_ROOT = os.path.expanduser(os.path.join("~", ".local", "fastfetch-release"))
def asset_name(system=None, machine=None):
system = system or platform.system()
machine = (machine or platform.machine()).lower()
arm = machine in ("arm64", "aarch64") or machine.startswith("armv8")
arch = "aarch64" if arm else "amd64"
if system == "Linux":
return f"fastfetch-linux-{arch}.tar.gz"
if system == "Darwin":
return f"fastfetch-macos-{arch}.tar.gz"
if system == "Windows":
return f"fastfetch-windows-{arch}.zip"
raise SystemExit(f"install_fastfetch: no fastfetch release asset for {system}")
def binary_name(system=None):
return "fastfetch.exe" if (system or platform.system()) == "Windows" else "fastfetch"
def find_binary_dir(root, system=None):
system = system or platform.system()
want = binary_name(system)
for dirpath, _dirs, files in sorted(os.walk(root)):
if want in files and (system == "Windows" or os.path.basename(dirpath) == "bin"):
return dirpath
return None
def install_latest_release():
asset = asset_name()
url = RELEASE_URL.format(asset=asset)
if os.path.isdir(INSTALL_ROOT):
shutil.rmtree(INSTALL_ROOT)
os.makedirs(INSTALL_ROOT)
archive = os.path.join(INSTALL_ROOT, asset)
print(f"Downloading {url}", flush=True)
urllib.request.urlretrieve(url, archive)
if asset.endswith(".zip"):
with zipfile.ZipFile(archive) as z:
z.extractall(INSTALL_ROOT)
else:
with tarfile.open(archive) as t:
if hasattr(tarfile, "data_filter"):
t.extractall(INSTALL_ROOT, filter="data")
else:
t.extractall(INSTALL_ROOT)
os.remove(archive)
bin_dir = find_binary_dir(INSTALL_ROOT)
if not bin_dir:
raise SystemExit(f"install_fastfetch: {binary_name()} not found in {asset}")
exe = os.path.join(bin_dir, binary_name())
if platform.system() != "Windows":
os.chmod(exe, 0o755)
version = subprocess.run([exe, "--version"], check=True, capture_output=True,
text=True).stdout.strip()
if not version.startswith("fastfetch "):
raise SystemExit(f"install_fastfetch: {exe} --version printed {version!r}, "
"which is not fastfetch")
print(f"Installed {version} at {exe}")
gh_path = os.environ.get("GITHUB_PATH")
if gh_path:
with open(gh_path, "a", encoding="utf-8") as f:
f.write(bin_dir + "\n")
print(f"Added {bin_dir} to GITHUB_PATH")
else:
print(f"Add {bin_dir} to PATH to use it.")
def install_from_package_manager():
system = platform.system()
candidates = {
"Darwin": [["brew", "install", "fastfetch"]],
"Windows": [
["winget", "install", "--id", "fastfetch-cli.fastfetch", "--silent",
"--accept-source-agreements"],
["scoop", "install", "fastfetch"],
["choco", "install", "fastfetch", "-y"],
],
"Linux": [["dnf", "install", "-y", "fastfetch"],
["sudo", "apt-get", "install", "-y", "fastfetch"]],
}.get(system, [])
for cmd in candidates:
if shutil.which(cmd[0] if cmd[0] != "sudo" else cmd[1]):
print(f"Installing fastfetch with {cmd[0] if cmd[0] != 'sudo' else cmd[1]}...")
if subprocess.run(cmd).returncode == 0:
return True
return False
def _self_test():
failures = []
cases = [
(("Linux", "x86_64"), "fastfetch-linux-amd64.tar.gz"),
(("Linux", "aarch64"), "fastfetch-linux-aarch64.tar.gz"),
(("Darwin", "arm64"), "fastfetch-macos-aarch64.tar.gz"),
(("Darwin", "x86_64"), "fastfetch-macos-amd64.tar.gz"),
(("Windows", "AMD64"), "fastfetch-windows-amd64.zip"),
(("Windows", "ARM64"), "fastfetch-windows-aarch64.zip"),
]
for (system, machine), want in cases:
got = asset_name(system, machine)
if got != want:
failures.append(f"asset_name({system}, {machine}) = {got}, want {want}")
if binary_name("Windows") != "fastfetch.exe" or binary_name("Linux") != "fastfetch":
failures.append("binary_name")
import tempfile
with tempfile.TemporaryDirectory() as tmp:
for rel in ("x/usr/aaa-completions/fastfetch", "x/usr/bin/fastfetch"):
os.makedirs(os.path.join(tmp, os.path.dirname(rel)), exist_ok=True)
open(os.path.join(tmp, rel), "w").close()
got = find_binary_dir(tmp, "Linux")
if got != os.path.join(tmp, "x", "usr", "bin"):
failures.append(f"find_binary_dir picked {got!r} over usr/bin")
with tempfile.TemporaryDirectory() as tmp:
open(os.path.join(tmp, "fastfetch.exe"), "w").close()
if find_binary_dir(tmp, "Windows") != tmp:
failures.append("find_binary_dir missed the top-level Windows exe")
if failures:
for f in failures:
print(f" FAIL {f}", file=sys.stderr)
return 1
print("install_fastfetch.py self-test passed")
return 0
def main(argv):
if argv == ["--self-test"]:
return _self_test()
if argv == ["--latest-release"]:
install_latest_release()
return 0
if argv:
print(__doc__, file=sys.stderr)
return 2
if shutil.which("fastfetch"):
print("fastfetch is already installed.")
return 0
if install_from_package_manager() and shutil.which("fastfetch"):
return 0
print("No package manager install; using the latest GitHub release.")
install_latest_release()
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))