archivebox_version.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #!/usr/bin/env python3
  2. __package__ = 'archivebox.cli'
  3. import sys
  4. import os
  5. import platform
  6. from pathlib import Path
  7. from typing import Iterable, Optional
  8. import rich_click as click
  9. from archivebox.misc.util import docstring, enforce_types
  10. @enforce_types
  11. def version(quiet: bool=False,
  12. binaries: Iterable[str]=()) -> list[str]:
  13. """Print the ArchiveBox version, debug metadata, and installed dependency versions"""
  14. # fast path for just getting the version and exiting, dont do any slower imports
  15. from archivebox.config.version import VERSION
  16. print(VERSION)
  17. if quiet or '--version' in sys.argv:
  18. return []
  19. from rich.panel import Panel
  20. from rich.console import Console
  21. from archivebox.config import CONSTANTS, DATA_DIR
  22. from archivebox.config.version import get_COMMIT_HASH, get_BUILD_TIME
  23. from archivebox.config.permissions import ARCHIVEBOX_USER, ARCHIVEBOX_GROUP, RUNNING_AS_UID, RUNNING_AS_GID, IN_DOCKER
  24. from archivebox.config.paths import get_data_locations, get_code_locations
  25. from archivebox.config.common import SHELL_CONFIG, STORAGE_CONFIG, SEARCH_BACKEND_CONFIG
  26. from archivebox.misc.logging_util import printable_folder_status
  27. from archivebox.config.configset import get_config
  28. console = Console()
  29. prnt = console.print
  30. # Check if LDAP is enabled (simple config lookup)
  31. config = get_config()
  32. LDAP_ENABLED = config.get('LDAP_ENABLED', False)
  33. p = platform.uname()
  34. COMMIT_HASH = get_COMMIT_HASH()
  35. prnt(
  36. '[dark_green]ArchiveBox[/dark_green] [dark_goldenrod]v{}[/dark_goldenrod]'.format(CONSTANTS.VERSION),
  37. f'COMMIT_HASH={COMMIT_HASH[:7] if COMMIT_HASH else "unknown"}',
  38. f'BUILD_TIME={get_BUILD_TIME()}',
  39. )
  40. prnt(
  41. f'IN_DOCKER={IN_DOCKER}',
  42. f'IN_QEMU={SHELL_CONFIG.IN_QEMU}',
  43. f'ARCH={p.machine}',
  44. f'OS={p.system}',
  45. f'PLATFORM={platform.platform()}',
  46. f'PYTHON={sys.implementation.name.title()}' + (' (venv)' if CONSTANTS.IS_INSIDE_VENV else ''),
  47. )
  48. try:
  49. OUTPUT_IS_REMOTE_FS = get_data_locations().DATA_DIR.is_mount or get_data_locations().ARCHIVE_DIR.is_mount
  50. except Exception:
  51. OUTPUT_IS_REMOTE_FS = False
  52. try:
  53. DATA_DIR_STAT = CONSTANTS.DATA_DIR.stat()
  54. prnt(
  55. f'EUID={os.geteuid()}:{os.getegid()} UID={RUNNING_AS_UID}:{RUNNING_AS_GID} PUID={ARCHIVEBOX_USER}:{ARCHIVEBOX_GROUP}',
  56. f'FS_UID={DATA_DIR_STAT.st_uid}:{DATA_DIR_STAT.st_gid}',
  57. f'FS_PERMS={STORAGE_CONFIG.OUTPUT_PERMISSIONS}',
  58. f'FS_ATOMIC={STORAGE_CONFIG.ENFORCE_ATOMIC_WRITES}',
  59. f'FS_REMOTE={OUTPUT_IS_REMOTE_FS}',
  60. )
  61. except Exception:
  62. prnt(
  63. f'EUID={os.geteuid()}:{os.getegid()} UID={RUNNING_AS_UID}:{RUNNING_AS_GID} PUID={ARCHIVEBOX_USER}:{ARCHIVEBOX_GROUP}',
  64. )
  65. prnt(
  66. f'DEBUG={SHELL_CONFIG.DEBUG}',
  67. f'IS_TTY={SHELL_CONFIG.IS_TTY}',
  68. f'SUDO={CONSTANTS.IS_ROOT}',
  69. f'ID={CONSTANTS.MACHINE_ID}:{CONSTANTS.COLLECTION_ID}',
  70. f'SEARCH_BACKEND={SEARCH_BACKEND_CONFIG.SEARCH_BACKEND_ENGINE}',
  71. f'LDAP={LDAP_ENABLED}',
  72. )
  73. prnt()
  74. if not (os.access(CONSTANTS.ARCHIVE_DIR, os.R_OK) and os.access(CONSTANTS.CONFIG_FILE, os.R_OK)):
  75. PANEL_TEXT = '\n'.join((
  76. '',
  77. '[violet]Hint:[/violet] [green]cd[/green] into a collection [blue]DATA_DIR[/blue] and run [green]archivebox version[/green] again...',
  78. ' [grey53]OR[/grey53] run [green]archivebox init[/green] to create a new collection in the current dir.',
  79. '',
  80. ' [i][grey53](this is [red]REQUIRED[/red] if you are opening a Github Issue to get help)[/grey53][/i]',
  81. '',
  82. ))
  83. 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]'))
  84. prnt()
  85. return []
  86. prnt('[pale_green1][i] Binary Dependencies:[/pale_green1]')
  87. failures = []
  88. # Setup Django before importing models
  89. from archivebox.config.django import setup_django
  90. setup_django()
  91. from archivebox.machine.models import Machine, Binary
  92. machine = Machine.current()
  93. # Get all binaries from the database
  94. all_installed = Binary.objects.filter(
  95. machine=machine
  96. ).exclude(abspath='').exclude(abspath__isnull=True).order_by('name')
  97. if not all_installed.exists():
  98. prnt('', '[grey53]No binaries detected. Run [green]archivebox install[/green] to detect dependencies.[/grey53]')
  99. else:
  100. for installed in all_installed:
  101. # Skip if user specified specific binaries and this isn't one
  102. if binaries and installed.name not in binaries:
  103. continue
  104. if installed.is_valid:
  105. display_path = installed.abspath.replace(str(DATA_DIR), '.').replace(str(Path('~').expanduser()), '~')
  106. version_str = (installed.version or 'unknown')[:15]
  107. provider = (installed.binprovider or 'env')[:8]
  108. prnt('', '[green]√[/green]', '', installed.name.ljust(18), version_str.ljust(16), provider.ljust(8), display_path, overflow='ignore', crop=False)
  109. else:
  110. prnt('', '[red]X[/red]', '', installed.name.ljust(18), '[grey53]not installed[/grey53]', overflow='ignore', crop=False)
  111. failures.append(installed.name)
  112. # Show hint if no binaries are installed yet
  113. has_any_installed = Binary.objects.filter(machine=machine).exclude(abspath='').exists()
  114. if not has_any_installed:
  115. prnt()
  116. prnt('', '[grey53]Run [green]archivebox install[/green] to detect and install dependencies.[/grey53]')
  117. if not binaries:
  118. # Show code and data locations
  119. prnt()
  120. prnt('[deep_sky_blue3][i] Code locations:[/deep_sky_blue3]')
  121. try:
  122. for name, path in get_code_locations().items():
  123. if isinstance(path, dict):
  124. prnt(printable_folder_status(name, path), overflow='ignore', crop=False)
  125. except Exception as e:
  126. prnt(f' [red]Error getting code locations: {e}[/red]')
  127. prnt()
  128. if os.access(CONSTANTS.ARCHIVE_DIR, os.R_OK) or os.access(CONSTANTS.CONFIG_FILE, os.R_OK):
  129. prnt('[bright_yellow][i] Data locations:[/bright_yellow]')
  130. try:
  131. for name, path in get_data_locations().items():
  132. if isinstance(path, dict):
  133. prnt(printable_folder_status(name, path), overflow='ignore', crop=False)
  134. except Exception as e:
  135. prnt(f' [red]Error getting data locations: {e}[/red]')
  136. try:
  137. from archivebox.misc.checks import check_data_dir_permissions
  138. check_data_dir_permissions()
  139. except Exception:
  140. pass
  141. else:
  142. prnt()
  143. prnt('[red][i] Data locations:[/red] (not in a data directory)')
  144. prnt()
  145. if failures:
  146. prnt('[red]Error:[/red] [yellow]Failed to detect the following binaries:[/yellow]')
  147. prnt(f' [red]{", ".join(failures)}[/red]')
  148. prnt()
  149. prnt('[violet]Hint:[/violet] To install missing binaries automatically, run:')
  150. prnt(' [green]archivebox install[/green]')
  151. prnt()
  152. return failures
  153. @click.command()
  154. @click.option('--quiet', '-q', is_flag=True, help='Only print ArchiveBox version number and nothing else. (equivalent to archivebox --version)')
  155. @click.option('--binaries', '-b', help='Select binaries to detect DEFAULT=curl,wget,git,yt-dlp,chrome,single-file,readability-extractor,postlight-parser,... (all)')
  156. @docstring(version.__doc__)
  157. def main(**kwargs):
  158. failures = version(**kwargs)
  159. if failures:
  160. raise SystemExit(1)
  161. if __name__ == '__main__':
  162. main()