| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- #!/usr/bin/env python3
- __package__ = 'archivebox.cli'
- import os
- import sys
- from typing import Optional, List
- import rich_click as click
- from rich import print
- from archivebox.misc.util import docstring, enforce_types
- @enforce_types
- def install(binproviders: Optional[List[str]]=None, binaries: Optional[List[str]]=None, dry_run: bool=False) -> None:
- """Automatically install all ArchiveBox dependencies and extras"""
-
- # if running as root:
- # - run init to create index + lib dir
- # - chown -R 911 DATA_DIR
- # - install all binaries as root
- # - chown -R 911 LIB_DIR
- # else:
- # - run init to create index + lib dir as current user
- # - install all binaries as current user
- # - recommend user re-run with sudo if any deps need to be installed as root
- import abx
- import archivebox
- from archivebox.config.permissions import IS_ROOT, ARCHIVEBOX_USER, ARCHIVEBOX_GROUP, SudoPermission
- from archivebox.config.paths import DATA_DIR, ARCHIVE_DIR, get_or_create_working_lib_dir
- from archivebox.misc.logging import stderr
- from archivebox.cli.archivebox_init import init
- from archivebox.misc.system import run as run_shell
- if not (os.access(ARCHIVE_DIR, os.R_OK) and ARCHIVE_DIR.is_dir()):
- init() # must init full index because we need a db to store InstalledBinary entries in
- print('\n[green][+] Installing ArchiveBox dependencies automatically...[/green]')
-
- # we never want the data dir to be owned by root, detect owner of existing owner of DATA_DIR to try and guess desired non-root UID
- if IS_ROOT:
- EUID = os.geteuid()
-
- # if we have sudo/root permissions, take advantage of them just while installing dependencies
- print()
- print(f'[yellow]:warning: Running as UID=[blue]{EUID}[/blue] with [red]sudo[/red] only for dependencies that need it.[/yellow]')
- print(f' DATA_DIR, LIB_DIR, and TMP_DIR will be owned by [blue]{ARCHIVEBOX_USER}:{ARCHIVEBOX_GROUP}[/blue].')
- print()
-
- LIB_DIR = get_or_create_working_lib_dir()
-
- package_manager_names = ', '.join(
- f'[yellow]{binprovider.name}[/yellow]'
- for binprovider in reversed(list(abx.as_dict(abx.pm.hook.get_BINPROVIDERS()).values()))
- if not binproviders or (binproviders and binprovider.name in binproviders)
- )
- print(f'[+] Setting up package managers {package_manager_names}...')
- for binprovider in reversed(list(abx.as_dict(abx.pm.hook.get_BINPROVIDERS()).values())):
- if binproviders and binprovider.name not in binproviders:
- continue
- try:
- binprovider.setup()
- except Exception:
- # it's ok, installing binaries below will automatically set up package managers as needed
- # e.g. if user does not have npm available we cannot set it up here yet, but once npm Binary is installed
- # the next package that depends on npm will automatically call binprovider.setup() during its own install
- pass
-
- print()
-
- for binary in reversed(list(abx.as_dict(abx.pm.hook.get_BINARIES()).values())):
- if binary.name in ('archivebox', 'django', 'sqlite', 'python'):
- # obviously must already be installed if we are running
- continue
-
- if binaries and binary.name not in binaries:
- continue
-
- providers = ' [grey53]or[/grey53] '.join(
- provider.name for provider in binary.binproviders_supported
- if not binproviders or (binproviders and provider.name in binproviders)
- )
- if not providers:
- continue
- print(f'[+] Detecting / Installing [yellow]{binary.name.ljust(22)}[/yellow] using [red]{providers}[/red]...')
- try:
- with SudoPermission(uid=0, fallback=True):
- # print(binary.load_or_install(fresh=True).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'}))
- if binproviders:
- providers_supported_by_binary = [provider.name for provider in binary.binproviders_supported]
- for binprovider_name in binproviders:
- if binprovider_name not in providers_supported_by_binary:
- continue
- try:
- if dry_run:
- # always show install commands when doing a dry run
- sys.stderr.write("\033[2;49;90m") # grey53
- result = binary.install(binproviders=[binprovider_name], dry_run=dry_run).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'})
- sys.stderr.write("\033[00m\n") # reset
- else:
- loaded_binary = archivebox.pm.hook.binary_load_or_install(binary=binary, binproviders=[binprovider_name], fresh=True, dry_run=dry_run, quiet=False)
- result = loaded_binary.model_dump(exclude={'overrides', 'bin_dir', 'hook_type'})
- if result and result['loaded_version']:
- break
- except Exception as e:
- print(f'[red]:cross_mark: Failed to install {binary.name} as using {binprovider_name} as user {ARCHIVEBOX_USER}: {e}[/red]')
- else:
- if dry_run:
- sys.stderr.write("\033[2;49;90m") # grey53
- binary.install(dry_run=dry_run).model_dump(exclude={'overrides', 'bin_dir', 'hook_type'})
- sys.stderr.write("\033[00m\n") # reset
- else:
- loaded_binary = archivebox.pm.hook.binary_load_or_install(binary=binary, fresh=True, dry_run=dry_run)
- result = loaded_binary.model_dump(exclude={'overrides', 'bin_dir', 'hook_type'})
- if IS_ROOT and LIB_DIR:
- with SudoPermission(uid=0):
- if ARCHIVEBOX_USER == 0:
- os.system(f'chmod -R 777 "{LIB_DIR.resolve()}"')
- else:
- os.system(f'chown -R {ARCHIVEBOX_USER} "{LIB_DIR.resolve()}"')
- except Exception as e:
- print(f'[red]:cross_mark: Failed to install {binary.name} as user {ARCHIVEBOX_USER}: {e}[/red]')
- if binaries and len(binaries) == 1:
- # if we are only installing a single binary, raise the exception so the user can see what went wrong
- raise
-
- from archivebox.config.django import setup_django
- setup_django()
-
- from django.contrib.auth import get_user_model
- User = get_user_model()
- if not User.objects.filter(is_superuser=True).exclude(username='system').exists():
- stderr('\n[+] Don\'t forget to create a new admin user for the Web UI...', color='green')
- stderr(' archivebox manage createsuperuser')
- # run_subcommand('manage', subcommand_args=['createsuperuser'], pwd=out_dir)
-
- print('\n[green][√] Set up ArchiveBox and its dependencies successfully.[/green]\n', file=sys.stderr)
-
- from abx_plugin_pip.binaries import ARCHIVEBOX_BINARY
-
- extra_args = []
- if binproviders:
- extra_args.append(f'--binproviders={",".join(binproviders)}')
- if binaries:
- extra_args.append(f'--binaries={",".join(binaries)}')
-
- proc = run_shell([ARCHIVEBOX_BINARY.load().abspath, 'version', *extra_args], capture_output=False, cwd=DATA_DIR)
- raise SystemExit(proc.returncode)
- @click.command()
- @click.option('--binproviders', '-p', type=str, help='Select binproviders to use DEFAULT=env,apt,brew,sys_pip,venv_pip,lib_pip,pipx,sys_npm,lib_npm,puppeteer,playwright (all)', default=None)
- @click.option('--binaries', '-b', type=str, help='Select binaries to install DEFAULT=curl,wget,git,yt-dlp,chrome,single-file,readability-extractor,postlight-parser,... (all)', default=None)
- @click.option('--dry-run', '-d', is_flag=True, help='Show what would be installed without actually installing anything', default=False)
- @docstring(install.__doc__)
- def main(**kwargs) -> None:
- install(**kwargs)
-
- if __name__ == '__main__':
- main()
|