diff --git a/.azure-pipelines/templates/jobs/packaging-jobs.yml b/.azure-pipelines/templates/jobs/packaging-jobs.yml index 1b8bcad68..55e603e54 100644 --- a/.azure-pipelines/templates/jobs/packaging-jobs.yml +++ b/.azure-pipelines/templates/jobs/packaging-jobs.yml @@ -59,7 +59,13 @@ jobs: versionSpec: 3.8 architecture: x86 addToPath: true - - script: python windows-installer/construct.py + - script: | + python -m venv venv + venv\Scripts\python tools\pipstrap.py + venv\Scripts\python tools\pip_install.py -e windows-installer + displayName: Prepare Windows installer build environment + - script: | + venv\Scripts\construct-windows-installer displayName: Build Certbot installer - task: CopyFiles@2 inputs: diff --git a/tools/pinning/pyproject.toml b/tools/pinning/pyproject.toml index 9df428361..9112fb054 100644 --- a/tools/pinning/pyproject.toml +++ b/tools/pinning/pyproject.toml @@ -32,9 +32,9 @@ certbot-nginx = {path = "../../certbot-nginx", extras = ["docs"]} certbot-apache = {path = "../../certbot-apache", extras = ["dev"]} certbot = {path = "../../certbot", extras = ["dev", "docs"]} acme = {path = "../../acme", extras = ["dev", "docs"]} +windows-installer = {path = "../../windows-installer"} # Extra dependencies -pynsist = "^2" # See https://github.com/certbot/certbot/issues/8425. mypy = "0.710" # Upgrading coverage, pylint, pytest, and some of pytest's plugins causes many diff --git a/tools/requirements.txt b/tools/requirements.txt index ca476a2fe..4c6c4825e 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -18,8 +18,8 @@ backcall==0.2.0 bcrypt==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" beautifulsoup4==4.9.3; python_version >= "3.6" and python_version < "4.0" bleach==3.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" -boto3==1.17.39; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" -botocore==1.20.39; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" +boto3==1.17.42; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" +botocore==1.20.42; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" cachecontrol==0.12.6; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" cached-property==1.5.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" cachetools==4.2.1; python_version >= "3.5" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") @@ -50,13 +50,13 @@ docutils==0.16; (python_version >= "2.7" and python_full_version < "3.0.0") or ( execnet==1.8.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" filelock==3.0.12; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "4.0" future==0.18.2; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" -google-api-core==1.26.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" -google-api-python-client==2.0.2; python_version >= "3.6" +google-api-core==1.26.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" +google-api-python-client==2.1.0; python_version >= "3.6" google-auth-httplib2==0.1.0; python_version >= "3.6" google-auth==1.28.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" googleapis-common-protos==1.53.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" html5lib==1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" -httplib2==0.19.0; python_version >= "3.6" +httplib2==0.19.1; python_version >= "3.6" idna==2.10; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_full_version >= "3.5.0" and python_version >= "3.6" and python_version < "4.0" imagesize==1.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.8" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") @@ -69,7 +69,7 @@ isodate==0.6.0; python_version >= "3.6" isort==4.3.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" jedi==0.18.0 jeepney==0.6.0; python_version >= "3.6" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") and sys_platform == "linux" -jinja2==2.11.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +jinja2==2.11.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" josepy==1.8.0; python_version >= "3.6" jsonlines==2.0.0; python_version >= "3.6" @@ -78,7 +78,7 @@ jsonschema==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or keyring==21.8.0; python_version >= "3.6" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0") lazy-object-proxy==1.4.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" lockfile==0.12.2 -markupsafe==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +markupsafe==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" mccabe==0.6.1; python_version >= "3.6" msgpack==1.0.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" msrest==0.6.21; python_version >= "3.6" @@ -89,7 +89,7 @@ oauthlib==3.1.0; python_version >= "3.6" and python_full_version < "3.0.0" or py packaging==20.9; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.6.0" paramiko==2.7.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" parsedatetime==2.6; python_version >= "3.6" -parso==0.8.1; python_version == "3.6" +parso==0.8.2; python_version == "3.6" pastel==0.2.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" pexpect==4.8.0 pickleshare==0.7.5 diff --git a/windows-installer/certbot.ico b/windows-installer/assets/certbot.ico similarity index 100% rename from windows-installer/certbot.ico rename to windows-installer/assets/certbot.ico diff --git a/windows-installer/renew-down.ps1 b/windows-installer/assets/renew-down.ps1 similarity index 100% rename from windows-installer/renew-down.ps1 rename to windows-installer/assets/renew-down.ps1 diff --git a/windows-installer/renew-up.ps1 b/windows-installer/assets/renew-up.ps1 similarity index 100% rename from windows-installer/renew-up.ps1 rename to windows-installer/assets/renew-up.ps1 diff --git a/windows-installer/run.bat b/windows-installer/assets/run.bat similarity index 100% rename from windows-installer/run.bat rename to windows-installer/assets/run.bat diff --git a/windows-installer/template.nsi b/windows-installer/assets/template.nsi similarity index 100% rename from windows-installer/template.nsi rename to windows-installer/assets/template.nsi diff --git a/windows-installer/setup.py b/windows-installer/setup.py new file mode 100644 index 000000000..01d75a99b --- /dev/null +++ b/windows-installer/setup.py @@ -0,0 +1,39 @@ +from setuptools import find_packages +from setuptools import setup + +version = '1.0' + +setup( + name='windows-installer', + version=version, + description='Environment to build the Certbot Windows installer', + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + python_requires='>=3.6', + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Operating System :: Microsoft :: Windows', + 'Topic :: Software Development :: Build Tools', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=[ + 'pynsist==2.7' + ], + entry_points={ + 'console_scripts': [ + 'construct-windows-installer = windows_installer.construct:main', + ], + }, +) diff --git a/windows-installer/windows_installer/__init__.py b/windows-installer/windows_installer/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/windows-installer/construct.py b/windows-installer/windows_installer/construct.py similarity index 92% rename from windows-installer/construct.py rename to windows-installer/windows_installer/construct.py index 21ee4c8be..91e988619 100644 --- a/windows-installer/construct.py +++ b/windows-installer/windows_installer/construct.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -import contextlib import ctypes import os import re @@ -7,7 +6,6 @@ import shutil import struct import subprocess import sys -import tempfile import time PYTHON_VERSION = (3, 8, 8) @@ -16,6 +14,21 @@ NSIS_VERSION = '3.06.1' def main(): + if 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)) + build_path, repo_path, venv_path, venv_python = _prepare_environment() _copy_assets(build_path, repo_path) @@ -81,11 +94,11 @@ def _copy_assets(build_path, repo_path): 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) - shutil.copy(os.path.join(repo_path, 'windows-installer', 'template.nsi'), build_path) - shutil.copy(os.path.join(repo_path, 'windows-installer', 'renew-up.ps1'), build_path) - shutil.copy(os.path.join(repo_path, 'windows-installer', 'renew-down.ps1'), build_path) + shutil.copy(os.path.join(repo_path, 'windows-installer', 'assets', 'certbot.ico'), build_path) + shutil.copy(os.path.join(repo_path, 'windows-installer', 'assets', 'run.bat'), build_path) + shutil.copy(os.path.join(repo_path, 'windows-installer', 'assets', 'template.nsi'), build_path) + shutil.copy(os.path.join(repo_path, 'windows-installer', 'assets', 'renew-up.ps1'), build_path) + shutil.copy(os.path.join(repo_path, 'windows-installer', 'assets', 'renew-down.ps1'), build_path) def _generate_pynsist_config(repo_path, build_path): @@ -150,18 +163,4 @@ def _prepare_environment(): if __name__ == '__main__': - if 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()