Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrien Ferrand <adferrand@users.noreply.github.com>2019-09-04 02:30:13 +0300
committerBrad Warren <bmw@users.noreply.github.com>2019-09-04 02:30:13 +0300
commited0b8e4af529958d33a5bd1f8ee5284fb1ab444b (patch)
treea2324d57252b168650a5af3ef42cba4d97fd8c7c /windows-installer
parent4eaa06d58e9d148bf2cb89231889e2709e86b71b (diff)
[Windows] Create an installer for Certbot (#7324)
This PR is the first step to create an official distribution channel of Certbot for Windows. It consists essentially in creating a proper Certbot Windows installer. Usually distributing an application requires, in a way or another, to stabilize the application logic and its dependencies around a given version. On Windows, this usually takes the form of a freezed application, that vendors its dependencies into a single executable. There are two well-known solutions to create an executable shipping a Python application on Windows: [py2exe](http://www.py2exe.org/) and [pyinstaller](https://www.pyinstaller.org/). However these solutions create self-executable `.EXE` files: you run the `.EXE` file that launches immediately the software. This is not a end-user solution. Indeed when a Windows user wants to install a piece of software, he expects to find and download an installer. When run the installer would interface with Windows to setup configuration entries in the Registry, update the environment variable, add shortcuts in the Start Menu, and declare a uninstaller entry into the Uninstaller Manager. Quite similarly, this is what you would get from a `.deb` or `.rpm` package. A solution that builds proper installers is [pynsis](https://pynsist.readthedocs.io/en/latest/). It is a Python project that constructs installers for Python software using [NSIS](https://sourceforge.net/projects/nsis/), the most known free Windows installer builder solution. This PR uses pynsist to build a Windows installer. The Python script to launch the installer build is `.\windows-installer\construct.py`. Once finished, the installer is located in `.\windows-installer\build\nsis`. This installer will do the following operations during the installation: * copy in the install path a full python distribution used exclusively for Certbot * copy all Python requirements gathered from the `setup.py` of relevant certbot projects * copy `certbot` and `acme` * pre-build python binary assets * register the existence of the application correctly in Windows Registry * prepare a procedure to uninstall Certbot * and of course, expose `certbot` executable to the Windows command line, like on Linux, to be able to launch it as any CLI application from Batch or Powershell This installer support updates: downloading a new version of it and running it on a Windows with existing installation of Certbot will replace it with the new version. Future capabilities not included in this PR: * auto-update of Certbot when a new release is available * online documentation for Windows * register a scheduled task for certificate renewal * installer distribution (continuous deployment + distribution channels) * method to check the downloaded installer is untampered * Setup config * Fix shortcut * Various improvments * Update windows-installer/construct.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Split into several method * Change installer name * Remove DNS plugins for now * Add a comment about administrator privileges * Update welcome * Control python version * Control bitness * Update windows-installer/construct.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Update windows-installer/construct.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Update windows-installer/construct.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>
Diffstat (limited to 'windows-installer')
-rw-r--r--windows-installer/.gitignore2
-rw-r--r--windows-installer/certbot.icobin0 -> 183198 bytes
-rw-r--r--windows-installer/construct.py137
-rw-r--r--windows-installer/run.bat31
4 files changed, 170 insertions, 0 deletions
diff --git a/windows-installer/.gitignore b/windows-installer/.gitignore
new file mode 100644
index 000000000..a1a48d6b8
--- /dev/null
+++ b/windows-installer/.gitignore
@@ -0,0 +1,2 @@
+build
+build.*
diff --git a/windows-installer/certbot.ico b/windows-installer/certbot.ico
new file mode 100644
index 000000000..364c32098
--- /dev/null
+++ b/windows-installer/certbot.ico
Binary files differ
diff --git a/windows-installer/construct.py b/windows-installer/construct.py
new file mode 100644
index 000000000..15296d559
--- /dev/null
+++ b/windows-installer/construct.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+import ctypes
+import struct
+import subprocess
+import os
+import sys
+import shutil
+import time
+
+
+PYTHON_VERSION = (3, 7, 4)
+PYTHON_BITNESS = 32
+
+
+def main():
+ build_path, repo_path, venv_path, venv_python = _prepare_environment()
+
+ _copy_assets(build_path, repo_path)
+
+ installer_cfg_path = _generate_pynsist_config(repo_path, build_path)
+
+ _prepare_build_tools(venv_path, venv_python)
+ _compile_wheels(repo_path, build_path, venv_python)
+ _build_installer(installer_cfg_path, venv_path)
+
+ print('Done')
+
+
+def _build_installer(installer_cfg_path, venv_path):
+ print('Build the installer')
+ subprocess.check_call([os.path.join(venv_path, 'Scripts', 'pynsist.exe'), installer_cfg_path])
+
+
+def _compile_wheels(repo_path, build_path, venv_python):
+ print('Compile wheels')
+
+ wheels_path = os.path.join(build_path, 'wheels')
+ os.makedirs(wheels_path)
+
+ certbot_packages = ['acme', '.']
+ # Uncomment following line to include all DNS plugins in the installer
+ # certbot_packages.extend([name for name in os.listdir(repo_path) if name.startswith('certbot-dns-')])
+ wheels_project = [os.path.join(repo_path, package) for package in certbot_packages]
+
+ command = [venv_python, '-m', 'pip', 'wheel', '-w', wheels_path]
+ command.extend(wheels_project)
+ subprocess.check_call(command)
+
+
+def _prepare_build_tools(venv_path, venv_python):
+ print('Prepare build tools')
+ subprocess.check_call([sys.executable, '-m', 'venv', venv_path])
+ subprocess.check_call(['choco', 'upgrade', '-y', 'nsis'])
+ subprocess.check_call([venv_python, '-m', 'pip', 'install', '--upgrade', 'pip'])
+ subprocess.check_call([venv_python, '-m', 'pip', 'install', 'wheel', 'pynsist'])
+
+
+def _copy_assets(build_path, repo_path):
+ print('Copy assets')
+ if os.path.exists(build_path):
+ os.rename(build_path, '{0}.{1}.bak'.format(build_path, int(time.time())))
+ os.makedirs(build_path)
+ shutil.copy(os.path.join(repo_path, 'windows-installer', 'certbot.ico'), build_path)
+ shutil.copy(os.path.join(repo_path, 'windows-installer', 'run.bat'), build_path)
+
+
+def _generate_pynsist_config(repo_path, build_path):
+ print('Generate pynsist configuration')
+
+ installer_cfg_path = os.path.join(build_path, 'installer.cfg')
+
+ certbot_version = subprocess.check_output([sys.executable, '-c', 'import certbot; print(certbot.__version__)'],
+ universal_newlines=True, cwd=repo_path).strip()
+
+ with open(os.path.join(installer_cfg_path), 'w') as file_h:
+ file_h.write("""\
+[Application]
+name=Certbot
+version={certbot_version}
+icon=certbot.ico
+publisher=Electronic Frontier Foundation
+target=$INSTDIR\\run.bat
+
+[Build]
+directory=nsis
+installer_name=certbot-{certbot_version}-installer-{installer_suffix}.exe
+
+[Python]
+version={python_version}
+bitness={python_bitness}
+
+[Include]
+local_wheels=wheels\\*.whl
+files=run.bat
+
+[Command certbot]
+entry_point=certbot.main:main
+""".format(certbot_version=certbot_version,
+ installer_suffix='win_amd64' if PYTHON_BITNESS == 64 else 'win32',
+ python_bitness=PYTHON_BITNESS,
+ python_version='.'.join([str(item) for item in PYTHON_VERSION])))
+
+ return installer_cfg_path
+
+
+def _prepare_environment():
+ print('Prepare environment')
+ try:
+ subprocess.check_output(['choco', '--version'])
+ except subprocess.CalledProcessError:
+ raise RuntimeError('Error: Chocolatey (https://chocolatey.org/) needs '
+ 'to be installed to run this script.')
+ script_path = os.path.realpath(__file__)
+ repo_path = os.path.dirname(os.path.dirname(script_path))
+ build_path = os.path.join(repo_path, 'windows-installer', 'build')
+ venv_path = os.path.join(build_path, 'venv-config')
+ venv_python = os.path.join(venv_path, 'Scripts', 'python.exe')
+
+ return build_path, repo_path, venv_path, venv_python
+
+
+if __name__ == '__main__':
+ if not os.name == 'nt':
+ raise RuntimeError('This script must be run under Windows.')
+
+ if ctypes.windll.shell32.IsUserAnAdmin() == 0:
+ # Administrator privileges are required to properly install NSIS through Chocolatey
+ raise RuntimeError('This script must be run with administrator privileges.')
+
+ if sys.version_info[:2] != PYTHON_VERSION[:2]:
+ raise RuntimeError('This script must be run with Python {0}'
+ .format('.'.join([str(item) for item in PYTHON_VERSION[0:2]])))
+
+ if struct.calcsize('P') * 8 != PYTHON_BITNESS:
+ raise RuntimeError('This script must be run with a {0} bit version of Python.'
+ .format(PYTHON_BITNESS))
+ main()
diff --git a/windows-installer/run.bat b/windows-installer/run.bat
new file mode 100644
index 000000000..efba28800
--- /dev/null
+++ b/windows-installer/run.bat
@@ -0,0 +1,31 @@
+@echo off
+
+:: BatchGotAdmin
+:-------------------------------------
+REM --> Check for permissions
+ IF "%PROCESSOR_ARCHITECTURE%" EQU "amd64" (
+>nul 2>&1 "%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system"
+) ELSE (
+>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
+)
+
+REM --> If error flag set, we do not have admin.
+if '%errorlevel%' NEQ '0' (
+ echo Requesting administrative privileges...
+ goto UACPrompt
+) else ( goto gotAdmin )
+
+:UACPrompt
+ echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
+ set params= %*
+ echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params:"=""%", "", "runas", 1 >> "%temp%\getadmin.vbs"
+
+ "%temp%\getadmin.vbs"
+ del "%temp%\getadmin.vbs"
+ exit /B
+
+:gotAdmin
+ pushd "%CD%"
+ CD /D "%~dp0"
+:--------------------------------------
+cmd.exe /k echo You can run 'certbot' commands here. Type 'certbot --help' for more information.