| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- #!/usr/bin/env python3
- __package__ = 'archivebox.cli'
- import sys
- from typing import Iterable
- import rich_click as click
- from archivebox.misc.util import docstring, enforce_types
- @enforce_types
- def version(quiet: bool=False,
- binproviders: Iterable[str]=(),
- binaries: Iterable[str]=()) -> list[str]:
- """Print the ArchiveBox version, debug metadata, and installed dependency versions"""
-
- # fast path for just getting the version and exiting, dont do any slower imports
- from archivebox.config.version import VERSION
- print(VERSION)
- if quiet or '--version' in sys.argv:
- return []
-
- # Only do slower imports when getting full version info
- import os
- import platform
- from pathlib import Path
-
- from rich.panel import Panel
- from rich.console import Console
- from abx_pkg import Binary
-
- import abx
- import archivebox
- from archivebox.config import CONSTANTS, DATA_DIR
- from archivebox.config.version import get_COMMIT_HASH, get_BUILD_TIME
- from archivebox.config.permissions import ARCHIVEBOX_USER, ARCHIVEBOX_GROUP, RUNNING_AS_UID, RUNNING_AS_GID, IN_DOCKER
- from archivebox.config.paths import get_data_locations, get_code_locations
- from archivebox.config.common import SHELL_CONFIG, STORAGE_CONFIG, SEARCH_BACKEND_CONFIG
- from archivebox.misc.logging_util import printable_folder_status
-
- from abx_plugin_default_binproviders import apt, brew, env
-
- console = Console()
- prnt = console.print
-
- LDAP_ENABLED = archivebox.pm.hook.get_SCOPE_CONFIG().LDAP_ENABLED
- # 0.7.1
- # ArchiveBox v0.7.1+editable COMMIT_HASH=951bba5 BUILD_TIME=2023-12-17 16:46:05 1702860365
- # IN_DOCKER=False IN_QEMU=False ARCH=arm64 OS=Darwin PLATFORM=macOS-14.2-arm64-arm-64bit PYTHON=Cpython
- # FS_ATOMIC=True FS_REMOTE=False FS_USER=501:20 FS_PERMS=644
- # DEBUG=False IS_TTY=True TZ=UTC SEARCH_BACKEND=ripgrep LDAP=False
-
- p = platform.uname()
- COMMIT_HASH = get_COMMIT_HASH()
- prnt(
- '[dark_green]ArchiveBox[/dark_green] [dark_goldenrod]v{}[/dark_goldenrod]'.format(CONSTANTS.VERSION),
- f'COMMIT_HASH={COMMIT_HASH[:7] if COMMIT_HASH else "unknown"}',
- f'BUILD_TIME={get_BUILD_TIME()}',
- )
- prnt(
- f'IN_DOCKER={IN_DOCKER}',
- f'IN_QEMU={SHELL_CONFIG.IN_QEMU}',
- f'ARCH={p.machine}',
- f'OS={p.system}',
- f'PLATFORM={platform.platform()}',
- f'PYTHON={sys.implementation.name.title()}' + (' (venv)' if CONSTANTS.IS_INSIDE_VENV else ''),
- )
- OUTPUT_IS_REMOTE_FS = get_data_locations().DATA_DIR.is_mount or get_data_locations().ARCHIVE_DIR.is_mount
- DATA_DIR_STAT = CONSTANTS.DATA_DIR.stat()
- prnt(
- f'EUID={os.geteuid()}:{os.getegid()} UID={RUNNING_AS_UID}:{RUNNING_AS_GID} PUID={ARCHIVEBOX_USER}:{ARCHIVEBOX_GROUP}',
- f'FS_UID={DATA_DIR_STAT.st_uid}:{DATA_DIR_STAT.st_gid}',
- f'FS_PERMS={STORAGE_CONFIG.OUTPUT_PERMISSIONS}',
- f'FS_ATOMIC={STORAGE_CONFIG.ENFORCE_ATOMIC_WRITES}',
- f'FS_REMOTE={OUTPUT_IS_REMOTE_FS}',
- )
- prnt(
- f'DEBUG={SHELL_CONFIG.DEBUG}',
- f'IS_TTY={SHELL_CONFIG.IS_TTY}',
- f'SUDO={CONSTANTS.IS_ROOT}',
- f'ID={CONSTANTS.MACHINE_ID}:{CONSTANTS.COLLECTION_ID}',
- f'SEARCH_BACKEND={SEARCH_BACKEND_CONFIG.SEARCH_BACKEND_ENGINE}',
- f'LDAP={LDAP_ENABLED}',
- #f'DB=django.db.backends.sqlite3 (({CONFIG["SQLITE_JOURNAL_MODE"]})', # add this if we have more useful info to show eventually
- )
- prnt()
-
- if not (os.access(CONSTANTS.ARCHIVE_DIR, os.R_OK) and os.access(CONSTANTS.CONFIG_FILE, os.R_OK)):
- PANEL_TEXT = '\n'.join((
- # '',
- # f'[yellow]CURRENT DIR =[/yellow] [red]{os.getcwd()}[/red]',
- '',
- '[violet]Hint:[/violet] [green]cd[/green] into a collection [blue]DATA_DIR[/blue] and run [green]archivebox version[/green] again...',
- ' [grey53]OR[/grey53] run [green]archivebox init[/green] to create a new collection in the current dir.',
- '',
- ' [i][grey53](this is [red]REQUIRED[/red] if you are opening a Github Issue to get help)[/grey53][/i]',
- '',
- ))
- prnt(Panel(PANEL_TEXT, expand=False, border_style='grey53', title='[red]:exclamation: No collection [blue]DATA_DIR[/blue] is currently active[/red]', subtitle='Full version info is only available when inside a collection [light_slate_blue]DATA DIR[/light_slate_blue]'))
- prnt()
- return []
- prnt('[pale_green1][i] Binary Dependencies:[/pale_green1]')
- failures = []
- BINARIES = abx.as_dict(archivebox.pm.hook.get_BINARIES())
- for name, binary in list(BINARIES.items()):
- if binary.name == 'archivebox':
- continue
-
- # skip if the binary is not in the requested list of binaries
- if binaries and binary.name not in binaries:
- continue
-
- # skip if the binary is not supported by any of the requested binproviders
- if binproviders and binary.binproviders_supported and not any(provider.name in binproviders for provider in binary.binproviders_supported):
- continue
-
- err = None
- try:
- loaded_bin = binary.load()
- except Exception as e:
- err = e
- loaded_bin = binary
- provider_summary = f'[dark_sea_green3]{loaded_bin.binprovider.name.ljust(10)}[/dark_sea_green3]' if loaded_bin.binprovider else '[grey23]not found[/grey23] '
- if loaded_bin.abspath:
- abspath = str(loaded_bin.abspath).replace(str(DATA_DIR), '[light_slate_blue].[/light_slate_blue]').replace(str(Path('~').expanduser()), '~')
- if ' ' in abspath:
- abspath = abspath.replace(' ', r'\ ')
- else:
- abspath = f'[red]{err}[/red]'
- prnt('', '[green]√[/green]' if loaded_bin.is_valid else '[red]X[/red]', '', loaded_bin.name.ljust(21), str(loaded_bin.version).ljust(12), provider_summary, abspath, overflow='ignore', crop=False)
- if not loaded_bin.is_valid:
- failures.append(loaded_bin.name)
-
- prnt()
- prnt('[gold3][i] Package Managers:[/gold3]')
- BINPROVIDERS = abx.as_dict(archivebox.pm.hook.get_BINPROVIDERS())
- for name, binprovider in list(BINPROVIDERS.items()):
- err = None
-
- if binproviders and binprovider.name not in binproviders:
- continue
-
- # TODO: implement a BinProvider.BINARY() method that gets the loaded binary for a binprovider's INSTALLER_BIN
- loaded_bin = binprovider.INSTALLER_BINARY or Binary(name=binprovider.INSTALLER_BIN, binproviders=[env, apt, brew])
-
- abspath = str(loaded_bin.abspath).replace(str(DATA_DIR), '[light_slate_blue].[/light_slate_blue]').replace(str(Path('~').expanduser()), '~')
- abspath = None
- if loaded_bin.abspath:
- abspath = str(loaded_bin.abspath).replace(str(DATA_DIR), '.').replace(str(Path('~').expanduser()), '~')
- if ' ' in abspath:
- abspath = abspath.replace(' ', r'\ ')
-
- PATH = str(binprovider.PATH).replace(str(DATA_DIR), '[light_slate_blue].[/light_slate_blue]').replace(str(Path('~').expanduser()), '~')
- ownership_summary = f'UID=[blue]{str(binprovider.EUID).ljust(4)}[/blue]'
- provider_summary = f'[dark_sea_green3]{str(abspath).ljust(52)}[/dark_sea_green3]' if abspath else f'[grey23]{"not available".ljust(52)}[/grey23]'
- prnt('', '[green]√[/green]' if binprovider.is_valid else '[grey53]-[/grey53]', '', binprovider.name.ljust(11), provider_summary, ownership_summary, f'PATH={PATH}', overflow='ellipsis', soft_wrap=True)
- if not (binaries or binproviders):
- # dont show source code / data dir info if we just want to get version info for a binary or binprovider
-
- prnt()
- prnt('[deep_sky_blue3][i] Code locations:[/deep_sky_blue3]')
- for name, path in get_code_locations().items():
- prnt(printable_folder_status(name, path), overflow='ignore', crop=False)
- prnt()
- if os.access(CONSTANTS.ARCHIVE_DIR, os.R_OK) or os.access(CONSTANTS.CONFIG_FILE, os.R_OK):
- prnt('[bright_yellow][i] Data locations:[/bright_yellow]')
- for name, path in get_data_locations().items():
- prnt(printable_folder_status(name, path), overflow='ignore', crop=False)
-
- from archivebox.misc.checks import check_data_dir_permissions
-
- check_data_dir_permissions()
- else:
- prnt()
- prnt('[red][i] Data locations:[/red] (not in a data directory)')
-
- prnt()
-
- if failures:
- prnt('[red]Error:[/red] [yellow]Failed to detect the following binaries:[/yellow]')
- prnt(f' [red]{", ".join(failures)}[/red]')
- prnt()
- prnt('[violet]Hint:[/violet] To install missing binaries automatically, run:')
- prnt(' [green]archivebox install[/green]')
- prnt()
- return failures
- @click.command()
- @click.option('--quiet', '-q', is_flag=True, help='Only print ArchiveBox version number and nothing else. (equivalent to archivebox --version)')
- @click.option('--binproviders', '-p', help='Select binproviders to detect DEFAULT=env,apt,brew,sys_pip,venv_pip,lib_pip,pipx,sys_npm,lib_npm,puppeteer,playwright (all)')
- @click.option('--binaries', '-b', help='Select binaries to detect DEFAULT=curl,wget,git,yt-dlp,chrome,single-file,readability-extractor,postlight-parser,... (all)')
- @docstring(version.__doc__)
- def main(**kwargs):
- failures = version(**kwargs)
- if failures:
- raise SystemExit(1)
- if __name__ == '__main__':
- main()
|