From 44472258cea9fb844471adf704494f34db5350ca Mon Sep 17 00:00:00 2001 From: Andres Perez Date: Wed, 23 Feb 2022 17:46:50 -0500 Subject: [PATCH] Change recommended installation method to pipx (#325) --- assets/installer.py | 393 -------------------------- content/contents.lr | 3 - content/docs/installation/contents.lr | 126 ++++----- content/downloads/contents.lr | 61 ++-- 4 files changed, 95 insertions(+), 488 deletions(-) delete mode 100755 assets/installer.py diff --git a/assets/installer.py b/assets/installer.py deleted file mode 100755 index 541281b0..00000000 --- a/assets/installer.py +++ /dev/null @@ -1,393 +0,0 @@ -#!/usr/bin/env python - -from __future__ import print_function -import math -import os -import shutil -import sys -import tempfile -from subprocess import call - -try: - from shutil import which -except ImportError: - from distutils.spawn import find_executable as which - -try: - from urllib.request import urlretrieve -except ImportError: - from urllib import urlretrieve - -IS_WIN = sys.platform == "win32" - -if IS_WIN: - try: - import winreg - except ImportError: - import _winreg as winreg - from ctypes import windll, wintypes - - -VIRTUALENV_URL = "https://bootstrap.pypa.io/virtualenv.pyz" - -# this difference is for backwards-compatibility with the previous installer -APP_NAME = "lektor" if not IS_WIN else "lektor-cli" - -# where to search for a writable bin directory on *nix. -# this order makes sure we try a system install first. -POSIX_BIN_DIRS = [ - "/usr/local/bin", "/opt/local/bin", - "{home}/.bin", "{home}/.local/bin", -] - -SILENT = ( - os.environ.get("LEKTOR_SILENT", "").lower() - not in ("", "0", "off", "false") -) - -if not os.isatty(sys.stdin.fileno()): - # the script is being piped, we need to reset stdin - sys.stdin = open("CON:" if IS_WIN else "/dev/tty") - -if sys.version_info.major == 2: - input = raw_input - - -def get_confirmation(): - if SILENT: - return - - while True: - user_input = input("Continue? [Yn] ").lower().strip() - - if user_input in ("", "y"): - print() - return - - if user_input == "n": - print() - print("Aborted!") - sys.exit() - - -def fail(message): - print("Error: %s" % message, file=sys.stderr) - sys.exit(1) - - -def multiprint(*lines, **kwargs): - for line in lines: - print(line, **kwargs) - - -def rm_recursive(*paths): - def _error(path): - multiprint( - "Problem deleting {}".format(path), - "Please try and delete {} manually".format(path), - "Aborted!", - file=sys.stderr, - ) - sys.exit(1) - - def _rm(path): - if os.path.isdir(path): - shutil.rmtree(path) - else: - os.remove(path) - - for path in paths: - if not os.path.lexists(path): - continue - try: - _rm(path) - except: - _error(path) - - -class Progress(object): - "A context manager to be used as a urlretrieve reporthook." - - def __init__(self): - self.started = False - - def progress(self, count, bsize, total): - size = count * bsize - - if size > total: - progress = 100 - else: - progress = math.floor(100 * size / total) - - out = sys.stdout - if self.started: - out.write("\b" * 4) - - out.write("{:3d}%".format(progress)) - out.flush() - - self.started = True - - def finish(self): - sys.stdout.write("\n") - - def __enter__(self): - return self.progress - - def __exit__(self, exc_type, exc_value, traceback): - self.finish() - - -class FetchTemp(object): - """ - Fetches the given URL into a temporary file. - To be used as a context manager. - """ - - def __init__(self, url): - self.url = url - - fname = os.path.basename(url) - root, ext = os.path.splitext(fname) - self.filename = tempfile.mktemp(prefix=root + "-", suffix=ext) - - def fetch(self): - with self.Progress() as hook: - urlretrieve(self.url, self.filename, reporthook=hook) - - def cleanup(self): - os.remove(self.filename) - - def __enter__(self): - self.fetch() - - return self.filename - - def __exit__(self, exc_type, exc_value, traceback): - self.cleanup() - - -def create_virtualenv(target_dir): - """ - Tries to create a virtualenv by using the built-in `venv` module, - or using the `virtualenv` executable if present, or falling back - to downloading the official zipapp. - """ - - def use_venv(): - try: - import venv - except ImportError: - return - - # on Debian and Ubuntu systems Python is missing `ensurepip`, - # prompting the user to install `python3-venv` instead. - # - # we could handle this, but we'll just let the command fail - # and have the users install the package themselves. - - return call([sys.executable, "-m", "venv", target_dir]) - - def use_virtualenv(): - venv_exec = which("virtualenv") - if not venv_exec: - return - - return call([venv_exec, "-p", sys.executable, target_dir]) - - def use_zipapp(): - print("Downloading virtualenv: ", end="") - with FetchTemp(VIRTUALENV_URL) as zipapp: - return call([sys.executable, zipapp, target_dir]) - - print("Installing virtual environment...") - for func in use_venv, use_virtualenv, use_zipapp: - retval = func() - if retval is None: - # command did not run - continue - if retval == 0: - # command successful - return - # else... - sys.exit(1) - - -def get_pip(lib_dir): - return ( - os.path.join(lib_dir, "Scripts", "pip.exe") if IS_WIN - else os.path.join(lib_dir, "bin", "pip") - ) - - -def install_lektor(lib_dir): - create_virtualenv(lib_dir) - - pip = get_pip(lib_dir) - - args = [pip, "install"] - if IS_WIN: - # avoid fail due to PEP 517 on windows - args.append("--prefer-binary") - args.extend(["--upgrade", "Lektor"]) - - return call(args) - - -def posix_find_bin_dir(): - home = os.environ["HOME"] - preferred = [d.format(home=home) for d in POSIX_BIN_DIRS] - - # look for writable directories in the user's $PATH - # (that are not sbin) - dirs = [ - item - for item in os.environ["PATH"].split(":") - if not item.endswith("/sbin") and os.access(item, os.W_OK) - ] - - if not dirs: - fail( - "None of the items in $PATH are writable. Run with " - "sudo or add a $PATH item that you have access to." - ) - - # ... and prioritize them according to our preferences - def _sorter(path): - try: - return preferred.index(path) - except ValueError: - return float("inf") - - dirs.sort(key=_sorter) - return dirs[0] - - -def posix_find_lib_dir(bin_dir): - # the chosen lib_dir depends on the bin_dir found: - home = os.environ["HOME"] - - if bin_dir.startswith(home): - # this is a local install - return os.path.join(home, ".local", "lib", APP_NAME) - - # else, it's a system install - parent = os.path.dirname(bin_dir) - return os.path.join(parent, "lib", APP_NAME) - - -def windows_create_link(lib_dir, target_dir): - exe = os.path.join(lib_dir, "Scripts", "lektor.exe") - link = os.path.join(target_dir, "lektor.cmd") - - with open(link, "w") as link_file: - link_file.write("@echo off\n") - link_file.write('"{}" %*'.format(exe)) - - -def windows_add_to_path(location): - HWND_BROADCAST = 0xFFFF - WM_SETTINGCHANGE = 0x1A - - key = winreg.OpenKey( - winreg.HKEY_CURRENT_USER, "Environment", 0, winreg.KEY_ALL_ACCESS - ) - - try: - value, _ = winreg.QueryValueEx(key, "Path") - except WindowsError: - value = "" - - paths = [path for path in value.split(";") if path != ""] - - if location not in paths: - paths.append(location) - value = ";".join(paths) - winreg.SetValueEx( - key, "Path", 0, winreg.REG_EXPAND_SZ, value - ) - - SendMessage = windll.user32.SendMessageW - SendMessage.argtypes = ( - wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPVOID - ) - SendMessage.restype = wintypes.LPARAM - SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, "Environment") - - # also add the path to the environment, - # so it's available in the current console - os.environ['Path'] += ";%s" % location - - key.Close() - - -def posix_install(): - bin_dir = posix_find_bin_dir() - lib_dir = posix_find_lib_dir(bin_dir) - symlink_path = os.path.join(bin_dir, APP_NAME) - - multiprint( - "Installing at:", - " bin: %s" % bin_dir, - " app: %s" % lib_dir, - "", - ) - - if os.path.exists(lib_dir) or os.path.lexists(symlink_path): - multiprint( - "An existing installation was detected. This will be removed!", - "", - ) - - get_confirmation() - rm_recursive(lib_dir, symlink_path) - install_lektor(lib_dir) - - os.symlink(os.path.join(lib_dir, "bin", "lektor"), symlink_path) - - -def windows_install(): - install_dir = os.path.join(os.environ["LocalAppData"], APP_NAME) - lib_dir = os.path.join(install_dir, "lib") - - multiprint( - "Installing at:", - " %s" % install_dir, - "", - ) - - if os.path.exists(install_dir): - multiprint( - "An existing installation was detected. This will be removed!", - "", - ) - - get_confirmation() - rm_recursive(install_dir) - install_lektor(lib_dir) - - windows_create_link(lib_dir, install_dir) - windows_add_to_path(install_dir) - - -def install(): - multiprint( - "", - "Welcome to Lektor", - "This script will install Lektor on your computer.", - "", - ) - - if IS_WIN: - windows_install() - else: - posix_install() - - multiprint( - "", - "All done!", - ) - - -if __name__ == "__main__": - install() diff --git a/content/contents.lr b/content/contents.lr index 9955e937..e876baf2 100644 --- a/content/contents.lr +++ b/content/contents.lr @@ -27,9 +27,6 @@ contents: > Download -
-
curl -sf https://www.getlektor.com/installer.py | sudo python3
-
#### text-block #### text: diff --git a/content/docs/installation/contents.lr b/content/docs/installation/contents.lr index 02171a0b..952054d2 100644 --- a/content/docs/installation/contents.lr +++ b/content/docs/installation/contents.lr @@ -6,86 +6,84 @@ sort_key: 50 --- body: -Lektor comes in two flavors: as a command line executable and as a -desktop application. The desktop version also contains the command -line executable, but it also bundles together all dependencies of Lektor -in an easy to use package which heavily simplifies installation. +Lektor is a command line utility, and requires some terminal or command line work +to install. If you can read and follow instructions, you will most likely have no +issues. -## Desktop Application +## Requirements -!! Info: Support for the Mac Desktop Application is paused as of version 3.1. See [note](/blog/2018/1/lektor-31-released/#support-for-mac-installer-paused). +Although `lektor` would work without Imagemagick or FFmpeg, you would be missing on +significant parts of its capabilities. Therefore, you need to make sure you have the +following software installed on your computer: -Currently the desktop application is only available for OS X and can be -[downloaded from the Lektor website](../../downloads/). It comes as a downloadable -disk image that you can mount which contains one application by the name of -`Lektor.app`. Just drag it into your `Applications` folder and you are good to -go. +* Python 3.6+ is required. If you got an older version, look at + {Python 3 Installation & Setup Guide}(https://realpython.com/installing-python/). + * On Ubuntu you basically don't need anything new. + * On macOS you're done as well. + * On Windows, make sure that Python is in your `PATH`. +* ImageMagick. + * On macOS `brew install imagemagick` can get you this. + * On Ubuntu `sudo apt install imagemagick` will solve it. + * On Windows do `choco install imagemagick`, which requires [chocolatey :ext](https://chocolatey.org/), + or [download from here :ext](http://www.imagemagick.org)). +* FFmpeg (mostly for video thumbnailing). + * On Ubuntu you get it through `sudo apt install ffmpeg.` + * On macOS `brew install ffmpeg` will do the trick. + * On Windows you'll have to put a little [work](https://www.wikihow.com/Install-FFmpeg-on-Windows) in. -If you also want access to the command line tools just launch `Lektor.app` -and then click in the menu bar on *Lektor ➤ Install Shell Command*. - -## Command Line Application - -If you do not want to install the desktop app then you can just install the command -line executable. This runs on most operating systems (OSX, Linux and Windows) but -the installation is a bit more involved. - -You need to make sure you have the following software installed on your computer: - -* Python 3 is recommended (but 2.7 is also supported) - On Ubuntu `python3-dev`, `libssl-dev` and `libffi-dev` are also required - `sudo apt-get install python3-dev libssl-dev libffi-dev` -* ImageMagick (`brew install imagemagick` can get you this on OS X and `sudo apt-get install imagemagick` - on Ubuntu the `imagemagick` package needs to be installed. - On Windows do `choco install imagemagick`, which requires [chocolatey :ext](https://chocolatey.org/), - or [download from here :ext](http://www.imagemagick.org)). +## Install pipx Once you have those installed and have made sure that they are on your `PATH`, you can -get Lektor installed with our installation script: +get Lektor installed with `pipx`. Selcet the `pipx` install method of your preference: +__On Ubuntu through `apt`.__ ``` -# curl -sf https://www.getlektor.com/installer.py | python3 +apt update +sudo apt install python3-venv pipx +pipx ensurepath +``` +__On Ubuntu through `pip`.__ +``` +sudo apt install python3-pip python3-venv +python3 -m pip install --user pipx +python3 -m pipx ensurepath +``` +__On macOS__ +``` +brew install pipx +pipx ensurepath +``` +__On Windows__ +``` +python -m pip install --user pipx +python -m pipx ensurepath ``` -This will attempt to install lektor in your user's `HOME`. If you want a system-wide installation, try this instead: +For more information on `pipx` installation go to [pypa.github.io/pipx/installation/](https://pypa.github.io/pipx/installation/) + +### Finally Install Lektor + +Once installed, close terminal (or `Powershell` or `command prompt`) for those +changes to take effect. Then proceed to reopen and install Lektor with pipx: ``` -$ curl -sf https://www.getlektor.com/installer.py | sudo python3 -``` - -If you would like to install Lektor without being prompted, set LEKTOR_SILENT before running the prior command. - -For Windows, make sure that Python is in your `PATH` and run in `Powershell`: - -``` -PS C:\> (new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python -``` - -or you can use the `command prompt` instead: - -``` -C:\> @powershell -NoProfile -Command "(new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python" +pipx install lektor ``` ## pip Alternatively you can manually install the command line version with -`virtualenv` if you know how that works. Note that this method is *heavily -discouraged* for anything other than advanced use cases such as build servers. +`pip` after creating a virtual environment, if you know how that works. `pipx` technically does this for you, +then if you need something particular and you know this will make it easier, +go for it. ``` -$ virtualenv venv -$ . venv/bin/activate -$ pip install Lektor +virtualenv venv +. venv/bin/activate +pip install Lektor ``` -!!!! When we say this installation type is discouraged we mean it. The reason -is that it encourages uses of Lektor which are entirely unsupported by us. -Lektor actively manages virtualenvs for plugin installations in very specific -ways and this might or might not work in your setup. We support pip -installations for deployment environments and local development only. - ## Development Version If you want to install the development version of Lektor you can do so. It's @@ -94,10 +92,10 @@ using PyPI you install directly from git and you need to have `npm` installed to build the admin UI: ``` -$ git clone https://github.com/lektor/lektor -$ cd lektor -$ make build-js -$ virtualenv venv -$ . venv/bin/activate -$ pip install --editable . +git clone https://github.com/lektor/lektor +cd lektor +make build-js +virtualenv venv +. venv/bin/activate +pip install --editable . ``` diff --git a/content/downloads/contents.lr b/content/downloads/contents.lr index ce42f148..c12eff1f 100644 --- a/content/downloads/contents.lr +++ b/content/downloads/contents.lr @@ -15,46 +15,51 @@ text: Lektor is an [Open Source Project](../license/) and freely available to download. It's currently still under heavy development and not all platforms -are equally well supported. +are equally well supported, although closer to it. ## Command Line Interface -If you are on Linux or Mac you can install the command line version of +You can install the command line version of Lektor by copy/pasting a command into your terminal. -!!! Scared about copy/pasting this into a terminal? We will not do anything -before asking you for confirmation and you can download [the script](https://www.getlektor.com/installer.py) -upfront to see what it's doing. +!!! Scared about copy/pasting this into a terminal? We now use standard tools +to install lektor. `pipx` is the recommended method to install standalone +command line interface python packages as recommended by [PyPA](https://packaging.python.org/guides/installing-stand-alone-command-line-tools/). -### Mac/Linux +### First Install PipX -This will install Lektor in your `HOME`. Running it with `sudo` will result in -a system installation instead. +Let's first install `pipx`. + +__Ubuntu__ +``` +sudo apt install python3-pip python3-venv +python3 -m pip install --user pipx +python3 -m pipx ensurepath +``` + +__macOS__ +``` +bew install pipx +pipx ensurepath +``` + +__Windows__ +``` +python -m pip install --user pipx +python -m pipx ensurepath +``` + +For more information on `pipx` installation go to [pypa.github.io/pipx/installation/](https://pypa.github.io/pipx/installation/) + +### Let's install Lektor now + +Close and reopen terminal (`Powershell` or `cmd` on Windows), then install `lektor`: ``` -curl -sf https://www.getlektor.com/installer.py | python3 +pipx install lektor ``` You might need additional dependencies for this installation. For more information see [Installation](../docs/installation/). - -### Windows - -If you are on Windows copy/paste this command into `Powershell`: - -``` -(new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python -``` - -or alternatively use this in `command prompt`: - -``` -@powershell -NoProfile -Command "(new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python" -``` - - -## Desktop Application - -Lektor supported an installable version of Lektor on OSX. The current build process for these installers is [old and in need of refactoring](https://github.com/lektor/lektor/issues/420). Temporarily until this is resolved, as of version 3.1, this installer is no longer supported. ---- class: default