Merge pull request #298 from lektor/unified-installer
Unified installer closes lektor/lektor#720 closes lektor/lektor#635 closes #267
This commit is contained in:
commit
1a059fa700
|
@ -1,173 +0,0 @@
|
||||||
$InstallScript = @"
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import tempfile
|
|
||||||
import tarfile
|
|
||||||
import shutil
|
|
||||||
from subprocess import Popen
|
|
||||||
try: # py3
|
|
||||||
from urllib.request import urlopen
|
|
||||||
from winreg import OpenKey, CloseKey, QueryValueEx, SetValueEx, \
|
|
||||||
HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_EXPAND_SZ
|
|
||||||
except ImportError: # py2
|
|
||||||
from urllib import urlopen
|
|
||||||
from _winreg import OpenKey, CloseKey, QueryValueEx, SetValueEx, \
|
|
||||||
HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_EXPAND_SZ
|
|
||||||
|
|
||||||
import ctypes
|
|
||||||
from ctypes.wintypes import HWND, UINT, WPARAM, LPARAM, LPVOID
|
|
||||||
|
|
||||||
|
|
||||||
VENV_URL = 'https://pypi.python.org/pypi/virtualenv/json'
|
|
||||||
APPDATA = os.environ['LocalAppData']
|
|
||||||
APP = 'lektor-cli'
|
|
||||||
LIB = 'lib'
|
|
||||||
ROOT_KEY = HKEY_CURRENT_USER
|
|
||||||
SUB_KEY = 'Environment'
|
|
||||||
LRESULT = LPARAM
|
|
||||||
HWND_BROADCAST = 0xFFFF
|
|
||||||
WM_SETTINGCHANGE = 0x1A
|
|
||||||
|
|
||||||
PY2 = sys.version_info[0] == 2
|
|
||||||
if PY2:
|
|
||||||
input = raw_input
|
|
||||||
|
|
||||||
|
|
||||||
def get_confirmation():
|
|
||||||
while 1:
|
|
||||||
user_input = input('Continue? [Yn] ').lower().strip()
|
|
||||||
if user_input in ('', 'y'):
|
|
||||||
break
|
|
||||||
elif user_input == 'n':
|
|
||||||
print()
|
|
||||||
print('Aborted!')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
def find_location():
|
|
||||||
install_dir = os.path.join(APPDATA, APP)
|
|
||||||
return install_dir, os.path.join(install_dir, LIB)
|
|
||||||
|
|
||||||
def deletion_error(func, path, excinfo):
|
|
||||||
print('Problem deleting {}'.format(path))
|
|
||||||
print('Please try and delete {} manually'.format(path))
|
|
||||||
print('Aborted!')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
def wipe_installation(install_dir):
|
|
||||||
shutil.rmtree(install_dir, onerror=deletion_error)
|
|
||||||
|
|
||||||
def check_installation(install_dir):
|
|
||||||
if os.path.exists(install_dir):
|
|
||||||
print(' Lektor seems to be installed already.')
|
|
||||||
print(' Continuing will delete:')
|
|
||||||
print(' %s' % install_dir)
|
|
||||||
print()
|
|
||||||
get_confirmation()
|
|
||||||
print()
|
|
||||||
wipe_installation(install_dir)
|
|
||||||
|
|
||||||
def fail(message):
|
|
||||||
print('Error: %s' % message)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def add_to_path(location):
|
|
||||||
reg_key = OpenKey(ROOT_KEY, SUB_KEY, 0, KEY_ALL_ACCESS)
|
|
||||||
|
|
||||||
try:
|
|
||||||
path_value, _ = QueryValueEx(reg_key, 'Path')
|
|
||||||
except WindowsError:
|
|
||||||
path_value = ''
|
|
||||||
|
|
||||||
paths = path_value.split(';')
|
|
||||||
if location not in paths:
|
|
||||||
paths.append(location)
|
|
||||||
path_value = ';'.join(paths)
|
|
||||||
SetValueEx(reg_key, 'Path', 0, REG_EXPAND_SZ, path_value)
|
|
||||||
|
|
||||||
SendMessage = ctypes.windll.user32.SendMessageW
|
|
||||||
SendMessage.argtypes = HWND, UINT, WPARAM, LPVOID
|
|
||||||
SendMessage.restype = LRESULT
|
|
||||||
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, u'Environment')
|
|
||||||
|
|
||||||
def _fetch_virtualenv():
|
|
||||||
for url in json.load(urlopen(VENV_URL))['urls']:
|
|
||||||
if url['python_version'] == 'source':
|
|
||||||
virtualenv_url = url['url']
|
|
||||||
#stripping '.tar.gz'
|
|
||||||
virtualenv_filename = url['filename'][:-7]
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
fail('Could not find virtualenv')
|
|
||||||
|
|
||||||
t = tempfile.mkdtemp()
|
|
||||||
with open(os.path.join(t, 'virtualenv.tar.gz'), 'wb') as f:
|
|
||||||
download = urlopen(virtualenv_url)
|
|
||||||
f.write(download.read())
|
|
||||||
download.close()
|
|
||||||
with tarfile.open(os.path.join(t, 'virtualenv.tar.gz'), 'r:gz') as tar:
|
|
||||||
tar.extractall(path=t)
|
|
||||||
|
|
||||||
return os.path.join(t, virtualenv_filename)
|
|
||||||
|
|
||||||
def install_virtualenv(target_dir):
|
|
||||||
# recent python versions include virtualenv
|
|
||||||
cmd = [sys.executable, '-m', 'venv', target_dir]
|
|
||||||
|
|
||||||
try:
|
|
||||||
import venv
|
|
||||||
except ImportError:
|
|
||||||
venv_dir = _fetch_virtualenv()
|
|
||||||
venv_file = os.path.join(venv_dir, 'virtualenv.py')
|
|
||||||
# in recent versions "virtualenv.py" moved to the "src" subdirectory
|
|
||||||
if not os.path.exists(venv_file):
|
|
||||||
venv_file = os.path.join(venv_dir, 'src', 'virtualenv.py')
|
|
||||||
|
|
||||||
cmd = [sys.executable, venv_file, target_dir]
|
|
||||||
|
|
||||||
Popen(cmd).wait()
|
|
||||||
|
|
||||||
def install(install_dir, lib_dir):
|
|
||||||
os.makedirs(install_dir)
|
|
||||||
os.makedirs(lib_dir)
|
|
||||||
|
|
||||||
install_virtualenv(lib_dir)
|
|
||||||
|
|
||||||
scripts = os.path.join(lib_dir, 'Scripts')
|
|
||||||
Popen([os.path.join(scripts, 'pip.exe'),
|
|
||||||
'install', '--upgrade', 'Lektor'],
|
|
||||||
cwd=scripts).wait()
|
|
||||||
|
|
||||||
with open(os.path.join(install_dir, 'lektor.cmd'), 'w') as link_file:
|
|
||||||
link_file.write('@echo off\n')
|
|
||||||
link_file.write('\"' + os.path.join(scripts, 'lektor.exe') + '\"' + ' %*')
|
|
||||||
|
|
||||||
add_to_path(install_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print()
|
|
||||||
print('Welcome to Lektor')
|
|
||||||
print()
|
|
||||||
print('This script will install Lektor on your computer.')
|
|
||||||
print()
|
|
||||||
|
|
||||||
install_dir, lib_dir = find_location()
|
|
||||||
|
|
||||||
check_installation(install_dir)
|
|
||||||
|
|
||||||
print(' Installing at:')
|
|
||||||
print(' %s' % install_dir)
|
|
||||||
print()
|
|
||||||
get_confirmation()
|
|
||||||
|
|
||||||
install(install_dir, lib_dir)
|
|
||||||
|
|
||||||
print()
|
|
||||||
print('All done!')
|
|
||||||
|
|
||||||
main()
|
|
||||||
"@
|
|
||||||
|
|
||||||
|
|
||||||
if (Get-Command python) { python -c $InstallScript } else { "To use this script you need to have Python installed"; exit }
|
|
|
@ -1,174 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# This script helps you install Lektor on your computer. Right now it
|
|
||||||
# only supports Linux and OS X and only on OS X will it install the
|
|
||||||
# desktop version.
|
|
||||||
#
|
|
||||||
# For more information see https://www.getlektor.com/
|
|
||||||
|
|
||||||
# Wrap everything in a function so that we do not accidentally execute
|
|
||||||
# something we should not in case a truncated version of the script
|
|
||||||
# is executed.
|
|
||||||
I() {
|
|
||||||
set -u
|
|
||||||
|
|
||||||
if ! hash python 2> /dev/null; then
|
|
||||||
echo "Error: To use this script you need to have Python installed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
python - <<'EOF'
|
|
||||||
if 1:
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import tempfile
|
|
||||||
import shutil
|
|
||||||
from subprocess import CalledProcessError, check_output, Popen
|
|
||||||
try:
|
|
||||||
from urllib.request import urlopen
|
|
||||||
except ImportError:
|
|
||||||
from urllib import urlopen
|
|
||||||
|
|
||||||
PY2 = sys.version_info[0] == 2
|
|
||||||
if PY2:
|
|
||||||
input = raw_input
|
|
||||||
|
|
||||||
sys.stdin = open('/dev/tty', 'r')
|
|
||||||
|
|
||||||
VENV_URL = "https://pypi.python.org/pypi/virtualenv/json"
|
|
||||||
KNOWN_BINS = ['/usr/local/bin', '/opt/local/bin',
|
|
||||||
os.path.join(os.environ['HOME'], '.bin'),
|
|
||||||
os.path.join(os.environ['HOME'], '.local', 'bin')]
|
|
||||||
|
|
||||||
if os.environ.get('LEKTOR_SILENT') == None:
|
|
||||||
prompt = True
|
|
||||||
else:
|
|
||||||
prompt = False
|
|
||||||
|
|
||||||
def find_user_paths():
|
|
||||||
rv = []
|
|
||||||
for item in os.environ['PATH'].split(':'):
|
|
||||||
if os.access(item, os.W_OK) \
|
|
||||||
and item not in rv \
|
|
||||||
and '/sbin' not in item:
|
|
||||||
rv.append(item)
|
|
||||||
return rv
|
|
||||||
|
|
||||||
def bin_sort_key(path):
|
|
||||||
try:
|
|
||||||
return KNOWN_BINS.index(path)
|
|
||||||
except ValueError:
|
|
||||||
return float('inf')
|
|
||||||
|
|
||||||
def find_locations(paths):
|
|
||||||
paths.sort(key=bin_sort_key)
|
|
||||||
for path in paths:
|
|
||||||
if path.startswith(os.environ['HOME']):
|
|
||||||
return path, os.path.join(os.environ['HOME'],
|
|
||||||
'.local', 'lib', 'lektor')
|
|
||||||
elif path.endswith('/bin'):
|
|
||||||
return path, os.path.join(
|
|
||||||
os.path.dirname(path), 'lib', 'lektor')
|
|
||||||
None, None
|
|
||||||
|
|
||||||
def get_confirmation():
|
|
||||||
while 1:
|
|
||||||
user_input = input('Continue? [Yn] ').lower().strip()
|
|
||||||
if user_input in ('', 'y'):
|
|
||||||
break
|
|
||||||
elif user_input == 'n':
|
|
||||||
print('')
|
|
||||||
print('Aborted!')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
def deletion_error(func, path, excinfo):
|
|
||||||
print('Problem deleting {}'.format(path))
|
|
||||||
print('Please try and delete {} manually'.format(path))
|
|
||||||
print('Aborted!')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
def wipe_installation(lib_dir, symlink_path):
|
|
||||||
if os.path.lexists(symlink_path):
|
|
||||||
os.remove(symlink_path)
|
|
||||||
if os.path.exists(lib_dir):
|
|
||||||
shutil.rmtree(lib_dir, onerror=deletion_error)
|
|
||||||
|
|
||||||
def check_installation(lib_dir, bin_dir):
|
|
||||||
symlink_path = os.path.join(bin_dir, 'lektor')
|
|
||||||
if os.path.exists(lib_dir) or os.path.lexists(symlink_path):
|
|
||||||
print(' Lektor seems to be installed already.')
|
|
||||||
print(' Continuing will delete:')
|
|
||||||
print(' %s' % lib_dir)
|
|
||||||
print(' and remove this symlink:')
|
|
||||||
print(' %s' % symlink_path)
|
|
||||||
print('')
|
|
||||||
if prompt:
|
|
||||||
get_confirmation()
|
|
||||||
print('')
|
|
||||||
wipe_installation(lib_dir, symlink_path)
|
|
||||||
|
|
||||||
def fail(message):
|
|
||||||
print('Error: %s' % message)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def install(virtualenv_url, lib_dir, bin_dir):
|
|
||||||
t = tempfile.mkdtemp()
|
|
||||||
Popen('curl -sf "%s" | tar -xzf - --strip-components=1' %
|
|
||||||
virtualenv_url, shell=True, cwd=t).wait()
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.makedirs(lib_dir)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
try: # virtualenv 16.1.0, 17+
|
|
||||||
check_output([sys.executable, './src/virtualenv.py', lib_dir], cwd=t)
|
|
||||||
except CalledProcessError: # older virtualenv
|
|
||||||
Popen([sys.executable, './virtualenv.py', lib_dir], cwd=t).wait()
|
|
||||||
Popen([os.path.join(lib_dir, 'bin', 'pip'),
|
|
||||||
'install', '--upgrade', 'Lektor']).wait()
|
|
||||||
os.symlink(os.path.join(lib_dir, 'bin', 'lektor'),
|
|
||||||
os.path.join(bin_dir, 'lektor'))
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print('')
|
|
||||||
print('Welcome to Lektor')
|
|
||||||
print('')
|
|
||||||
print('This script will install Lektor on your computer.')
|
|
||||||
print('')
|
|
||||||
|
|
||||||
paths = find_user_paths()
|
|
||||||
if not paths:
|
|
||||||
fail('None of the items in $PATH are writable. Run with '
|
|
||||||
'sudo or add a $PATH item that you have access to.')
|
|
||||||
|
|
||||||
bin_dir, lib_dir = find_locations(paths)
|
|
||||||
if bin_dir is None or lib_dir is None:
|
|
||||||
fail('Could not determine installation location for Lektor.')
|
|
||||||
|
|
||||||
check_installation(lib_dir, bin_dir)
|
|
||||||
|
|
||||||
print('Installing at:')
|
|
||||||
print(' bin: %s' % bin_dir)
|
|
||||||
print(' app: %s' % lib_dir)
|
|
||||||
print('')
|
|
||||||
|
|
||||||
if prompt: get_confirmation()
|
|
||||||
|
|
||||||
for url in json.loads(urlopen(VENV_URL).read().decode('utf-8'))['urls']:
|
|
||||||
if url['python_version'] == 'source':
|
|
||||||
virtualenv = url['url']
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
fail('Could not find virtualenv')
|
|
||||||
|
|
||||||
install(virtualenv, lib_dir, bin_dir)
|
|
||||||
|
|
||||||
print('')
|
|
||||||
print('All done!')
|
|
||||||
|
|
||||||
main()
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
I
|
|
|
@ -0,0 +1,393 @@
|
||||||
|
#!/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()
|
|
@ -32,9 +32,9 @@ the installation is a bit more involved.
|
||||||
|
|
||||||
You need to make sure you have the following software installed on your computer:
|
You need to make sure you have the following software installed on your computer:
|
||||||
|
|
||||||
* Python 2.7 or above (also `python-dev`, `libssl-dev` and
|
* Python 3 is recommended (but 2.7 is also supported)
|
||||||
`libffi-dev` is required on Ubuntu)
|
On Ubuntu `python3-dev`, `libssl-dev` and `libffi-dev` are also required
|
||||||
`sudo apt-get install python-dev libssl-dev libffi-dev`
|
`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`
|
* 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 Ubuntu the `imagemagick` package needs to be installed.
|
||||||
On Windows do `choco install imagemagick`, which requires [chocolatey :ext](https://chocolatey.org/),
|
On Windows do `choco install imagemagick`, which requires [chocolatey :ext](https://chocolatey.org/),
|
||||||
|
@ -44,24 +44,30 @@ Once you have those installed and have made sure that they are on your `PATH`, y
|
||||||
get Lektor installed with our installation script:
|
get Lektor installed with our installation script:
|
||||||
|
|
||||||
```
|
```
|
||||||
# curl -sf https://www.getlektor.com/install.sh | sh
|
# curl -sf https://www.getlektor.com/installer.py | python3
|
||||||
```
|
```
|
||||||
If you are not logged in as superuser, instead you should try this:
|
|
||||||
|
This will attempt to install lektor in your user's `HOME`. If you want a system-wide installation, try this instead:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -sf https://www.getlektor.com/install.sh | sudo sh
|
$ 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.
|
If you would like to install Lektor without being prompted, set LEKTOR_SILENT before running the prior command.
|
||||||
|
|
||||||
For Windows you can use the `command prompt`:
|
For Windows, make sure that Python is in your `PATH` and run in `Powershell`:
|
||||||
|
|
||||||
```
|
```
|
||||||
C:\> @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://getlektor.com/install.ps1'))" && SET PATH=%PATH%;%LocalAppData%\lektor-cli
|
PS C:\> (new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python
|
||||||
```
|
```
|
||||||
but you can also do it directly in `Powershell`:
|
|
||||||
|
or you can use the `command prompt` instead:
|
||||||
|
|
||||||
```
|
```
|
||||||
PS C:\> iex ((new-object net.webclient).DownloadString('https://getlektor.com/install.ps1'))
|
C:\> @powershell -NoProfile -Command "(new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## pip
|
## pip
|
||||||
|
|
||||||
Alternatively you can manually install the command line version with
|
Alternatively you can manually install the command line version with
|
||||||
|
|
|
@ -23,16 +23,16 @@ If you are on Linux or Mac you can install the command line version of
|
||||||
Lektor by copy/pasting a command into your terminal.
|
Lektor by copy/pasting a command into your terminal.
|
||||||
|
|
||||||
!!! Scared about copy/pasting this into a terminal? We will not do anything
|
!!! Scared about copy/pasting this into a terminal? We will not do anything
|
||||||
before asking you for confirmation and you can download the script upfront
|
before asking you for confirmation and you can download [the script](https://www.getlektor.com/installer.py)
|
||||||
to see what it's doing.
|
upfront to see what it's doing.
|
||||||
|
|
||||||
### Mac/Linux
|
### Mac/Linux
|
||||||
|
|
||||||
This will install Lektor for you but you might have to run it with `sudo` if
|
This will install Lektor in your `HOME`. Running it with `sudo` will result in
|
||||||
your current user does not have rights to write into `/usr/local`.
|
a system installation instead.
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -sf https://www.getlektor.com/install.sh | sh
|
curl -sf https://www.getlektor.com/installer.py | python3
|
||||||
```
|
```
|
||||||
|
|
||||||
You might need additional dependencies for this installation. For more
|
You might need additional dependencies for this installation. For more
|
||||||
|
@ -40,18 +40,19 @@ information see [Installation](../docs/installation/).
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
If you are on Windows copy/paste this command into the `command prompt`:
|
If you are on Windows copy/paste this command into `Powershell`:
|
||||||
|
|
||||||
```
|
```
|
||||||
@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://getlektor.com/install.ps1'))" && SET PATH=%PATH%;%LocalAppData%\lektor-cli
|
(new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python
|
||||||
```
|
```
|
||||||
|
|
||||||
alternatively use this command in your `Powershell`:
|
or alternatively use this in `command prompt`:
|
||||||
|
|
||||||
```
|
```
|
||||||
iex ((new-object net.webclient).DownloadString('https://getlektor.com/install.ps1'))
|
@powershell -NoProfile -Command "(new-object net.webclient).DownloadString('https://www.getlektor.com/installer.py') | python"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Desktop Application
|
## 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.
|
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.
|
||||||
|
|
Loading…
Reference in New Issue