Compare commits

...

34 Commits

Author SHA1 Message Date
Adrien Ferrand
814223beaf Merge branch 'master' into windows-auto-update 2020-03-31 22:31:58 +02:00
Adrien Ferrand
136621ba40 Merge branch 'master' into windows-auto-update 2020-03-31 13:04:37 +02:00
Adrien Ferrand
495d7dd6b6 Update windows-installer/tasks-up.ps1
Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>
2020-03-31 02:01:48 +02:00
Adrien Ferrand
7fccf83ce1 Refactor values 2020-03-31 02:01:15 +02:00
Adrien Ferrand
7a6c04eb41 Rename function 2020-03-31 01:58:35 +02:00
Adrien Ferrand
1443a723aa Instantiate System.Version object 2020-03-31 01:56:13 +02:00
Adrien Ferrand
8110b8107a Check powershell is 5.0+ before starting the installation. 2020-03-31 01:52:01 +02:00
Adrien Ferrand
54064d6ee1 Merge branch 'master' into windows-auto-update 2020-03-30 22:23:37 +02:00
Adrien Ferrand
d299afa4ad Merge branch 'master' into windows-auto-update 2020-02-24 19:59:49 +01:00
Adrien Ferrand
689fc9909c Windows does not like "&" 2020-02-21 22:11:30 +01:00
Adrien Ferrand
067438f3c2 Merge branch 'master' into windows-auto-update 2020-02-21 22:00:31 +01:00
Adrien Ferrand
d395a569b7 Merge branch 'master' into windows-auto-update 2020-02-13 21:45:50 +01:00
Adrien Ferrand
d90b0c2b20 Enable signing check. Fix logs. 2020-02-13 21:45:22 +01:00
Adrien Ferrand
6e8e6e4d48 Add comments 2020-02-13 21:08:19 +01:00
Adrien Ferrand
2dbb121e35 Finish integration tests 2020-02-13 17:02:38 +01:00
Adrien Ferrand
a37acc90eb Continue configuration 2020-02-11 16:13:27 +01:00
Adrien Ferrand
46fa2620ee Work in progress 2020-02-10 18:04:32 +01:00
Adrien Ferrand
1879c32669 Merge branch 'master' into windows-auto-update 2020-02-10 14:18:23 +01:00
Adrien Ferrand
f5f5c9eba6 Use public key to validate the authenticode of the installer 2020-01-09 21:39:37 +01:00
Adrien Ferrand
f86a1bde9d Merge branch 'master' into windows-auto-update 2019-12-18 19:54:15 +01:00
Adrien Ferrand
4583df2cd2 Use event log 2019-12-16 22:46:31 +01:00
Adrien Ferrand
725eb7a2b8 Install Python manually 2019-12-12 14:54:42 +01:00
Adrien Ferrand
b6300cbdbb Try specific installer-build step 2019-12-12 01:27:42 +01:00
Adrien Ferrand
2a9505927a Increase compatibility 2019-12-11 23:57:37 +01:00
Adrien Ferrand
1b16d64b9f Update task description 2019-12-11 22:42:19 +01:00
Adrien Ferrand
1b55099059 Update scripts 2019-12-11 21:43:12 +01:00
Adrien Ferrand
dbc37ddac9 Merge branch 'master' into windows-auto-update 2019-12-11 21:17:28 +01:00
Adrien Ferrand
ec7ecd6b40 Uninstall before 2019-11-19 23:18:31 +01:00
Adrien Ferrand
56ad2a3346 Various corrections 2019-11-19 21:26:56 +01:00
Adrien Ferrand
a896d16625 Scripts are not executables, they exist only in the interpreter memory during execution. 2019-11-19 15:07:20 +01:00
Adrien Ferrand
c02c14e18e Merge branch 'master' into windows-auto-update
# Conflicts:
#	windows-installer/renew-up.ps1
2019-11-19 15:05:51 +01:00
Adrien Ferrand
b2e7219411 Being pedantic 2019-11-12 15:21:23 +01:00
Adrien Ferrand
8f1380a90a Ready for review 2019-11-12 00:55:11 +01:00
Adrien Ferrand
7cc0a02a7c Prepare logic 2019-11-12 00:00:26 +01:00
10 changed files with 591 additions and 144 deletions

View File

@@ -3,57 +3,211 @@ import time
import unittest
import subprocess
import re
from http.server import BaseHTTPRequestHandler
import threading
import socketserver
import json
import tempfile
import shutil
import warnings
import pytest
with warnings.catch_warnings():
warnings.simplefilter("ignore")
import pkg_resources
SCHEDULED_TASK_NAME = 'Certbot Renew and Auto-Update Task'
@pytest.fixture
def signing_cert():
"""
This fixture returns the path of a test signing certificate that is loaded into the
Trusted Root Certification Authorities group of the Windows certificate store, in order
to make Windows accept any executable signed with this certificate.
Fixture cleanup is included.
"""
cert_thumbprint = None
try:
pfx_file = pkg_resources.resource_filename('windows_installer_integration_tests', 'assets/test-signing.pfx')
output = _ps('(Import-PfxCertificate -FilePath {0} -CertStoreLocation Cert:\\LocalMachine\\Root).Thumbprint'
.format(pfx_file), capture_stdout=True)
cert_thumbprint = output.strip()
if not cert_thumbprint:
raise RuntimeError('Error, test signing certificate could not be installed.')
yield pfx_file
finally:
if cert_thumbprint:
_ps('Get-ChildItem Cert:\\LocalMachine\\Root\\{0} | Remove-Item'.format(cert_thumbprint))
@pytest.fixture
def installer(request, signing_cert):
"""
This fixture returns the path of the Certbot Windows installer to use during the tests.
It is signed with a test signing certificate that is accepted by the current system and
thus the installer has a valid Authenticode status.
Fixture cleanup is included.
"""
with tempfile.TemporaryDirectory() as temp_dir:
shutil.copy(request.config.option.installer_path, temp_dir)
installer_path = os.path.join(temp_dir, os.path.basename(request.config.option.installer_path))
_ps('Set-AuthenticodeSignature -FilePath {0} -Certificate (Get-PfxCertificate -FilePath {1}) | Out-Null'
.format(installer_path, signing_cert))
yield installer_path
@pytest.fixture
def github_mock(installer):
"""
This fixture starts a GitHub release API mock on localhost:9001. This mock returns a
compliant GitHub release payload declaring that Certbot v99.9.9 is available.
The assets path associated allows to download on localhost the signed Certbot installer
used during the tests.
"""
server = None
try:
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class GitHubMock(BaseHTTPRequestHandler):
def log_message(self, log_format, *args):
pass
def do_GET(self):
if re.match(r'^.*/releases/latest$', self.path):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(
{
'tag_name': 'v99.9.9',
'assets': [{
'name': os.path.basename(installer),
'browser_download_url': 'http://localhost:8009/{0}'.format(os.path.basename(installer))
}]
}
).encode())
elif re.match(r'^.*certbot-.*installer-win32\.exe$', self.path):
self.send_response(200)
self.send_header('Content-type', 'application/octet-stream')
self.end_headers()
with open(installer, 'rb') as file_h:
self.wfile.write(file_h.read())
else:
self.send_response(404)
self.end_headers()
server_address = ('', 8009)
server = ThreadedTCPServer(server_address, GitHubMock)
thread = threading.Thread(target=server.serve_forever)
thread.daemon = True
thread.start()
yield 'http://localhost:8009/releases/latest'
finally:
if server:
server.shutdown()
server.server_close()
@pytest.fixture
def upgrade_env(signing_cert, github_mock):
"""
This fixture prepares the current Windows system Registry for a proper blackbox testing
of the auto-upgrade mechanism. GitHub release API is set to use the local GitHub release
API mock. And the public key used to validate the installer is set to be the one from
the test signing certificate used in the tests.
Fixture cleanup is included.
"""
try:
_ps('New-Item -Path HKLM:\\Software -Name Certbot -ErrorAction SilentlyContinue | Out-Null; exit 0')
_ps('New-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotUpgradeApiURL -Value {} '
'| Out-Null'.format(github_mock))
_ps('New-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotSigningPubKey -Value '
'([Convert]::ToBase64String((Get-PfxCertificate -FilePath {0}).GetPublicKey())) '
'| Out-Null'.format(signing_cert))
yield True
finally:
_ps('Remove-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotUpgradeApiURL')
_ps('Remove-ItemProperty -Path HKLM:\\Software\\Certbot -Name CertbotSigningPubKey')
@unittest.skipIf(os.name != 'nt', reason='Windows installer tests must be run on Windows.')
def test_it(request):
def test_base(installer):
"""
This test checks that the Certbot installer installs correctly Certbot, including a fully
functional automated renewal mechanism through a Windows scheduled task.
"""
_assert_certbot_is_missing()
# Install certbot
subprocess.check_output([installer, '/S'])
# Assert certbot is installed and runnable
output = subprocess.check_output('certbot --version', shell=True, universal_newlines=True)
assert re.match(r'^certbot \d+\.\d+\.\d+.*$', output), 'Flag --version does not output a version.'
# Assert the renew + auto-upgrade task is installed and ready
output = _ps('(Get-ScheduledTask -TaskName "{}").State'.format(SCHEDULED_TASK_NAME), capture_stdout=True)
assert output.strip() == 'Ready'
# Trigger the renew + auto-upgrade task, expecting Certbot to check for certificate renewals.
now = time.time()
_ps('Start-ScheduledTask -TaskName "{}"'.format(SCHEDULED_TASK_NAME))
_wait_for_task_completion()
log_path = os.path.join('C:\\', 'Certbot', 'log', 'letsencrypt.log')
modification_time = os.path.getmtime(log_path)
assert now < modification_time, 'Certbot log file has not been modified by the renew task.'
with open(log_path) as file_h:
data = file_h.read()
assert 'no renewal failures' in data, 'Renew task did not execute properly.'
# NB: This test must be declared after test_base, and so will be started after test_base,
# because it requires a working installation of Certbot, and test_base provides that.
@unittest.skipIf(os.name != 'nt', reason='Windows installer tests must be run on Windows.')
def test_upgrade(upgrade_env):
"""
This tests checks that Certbot installed with the current tested installer can upgrade
or repair itself through a Windows scheduled task.
"""
assert upgrade_env
subprocess.check_output(['certbot', '--version'])
# Break Certbot on purpose
_ps('Remove-Item "${env:ProgramFiles(x86)}\\Certbot\\bin\\certbot.exe" -Confirm:$false')
_assert_certbot_is_missing()
# Trigger the renew + auto-upgrade task, expecting Certbot to be reinstalled and functional again.
now = time.time()
_ps('Start-ScheduledTask -TaskName "{}"'.format(SCHEDULED_TASK_NAME))
_wait_for_task_completion()
subprocess.check_output(['certbot', '--version'])
def _assert_certbot_is_missing():
try:
subprocess.check_call(['certbot', '--version'])
subprocess.check_output(['certbot', '--version'])
except (subprocess.CalledProcessError, OSError):
pass
else:
raise AssertionError('Expect certbot to not be available in the PATH.')
try:
# Install certbot
subprocess.check_call([request.config.option.installer_path, '/S'])
# Assert certbot is installed and runnable
output = subprocess.check_output(['certbot', '--version'], universal_newlines=True)
assert re.match(r'^certbot \d+\.\d+\.\d+.*$', output), 'Flag --version does not output a version.'
# Assert renew task is installed and ready
output = _ps('(Get-ScheduledTask -TaskName "Certbot Renew Task").State', capture_stdout=True)
assert output.strip() == 'Ready'
# Assert renew task is working
now = time.time()
_ps('Start-ScheduledTask -TaskName "Certbot Renew Task"')
status = 'Running'
while status != 'Ready':
status = _ps('(Get-ScheduledTask -TaskName "Certbot Renew Task").State', capture_stdout=True).strip()
time.sleep(1)
log_path = os.path.join('C:\\', 'Certbot', 'log', 'letsencrypt.log')
modification_time = os.path.getmtime(log_path)
assert now < modification_time, 'Certbot log file has not been modified by the renew task.'
with open(log_path) as file_h:
data = file_h.read()
assert 'no renewal failures' in data, 'Renew task did not execute properly.'
finally:
# Sadly this command cannot work in non interactive mode: uninstaller will ask explicitly permission in an UAC prompt
# print('Uninstalling Certbot ...')
# uninstall_path = _ps('(gci "HKLM:\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"'
# ' | foreach { gp $_.PSPath }'
# ' | ? { $_ -match "Certbot" }'
# ' | select UninstallString)'
# '.UninstallString', capture_stdout=True)
# subprocess.check_call([uninstall_path, '/S'])
pass
def _wait_for_task_completion():
status = 'Running'
while status != 'Ready':
status = _ps('(Get-ScheduledTask -TaskName "{}").State'
.format(SCHEDULED_TASK_NAME), capture_stdout=True).strip()
time.sleep(1)
def _ps(powershell_str, capture_stdout=False):

View File

@@ -0,0 +1,139 @@
#Requires -Version 5.0
[CmdletBinding()]
param()
begin {}
process {
$eventSource = "certbot/auto-update.ps1"
$logName = "CertbotAutoUpdate"
$eventID = 1
New-EventLog -Source $eventSource -LogName $logName -ErrorAction SilentlyContinue
function Write-Message($message, $level = "Information") {
Write-EventLog -Source $eventSource -LogName $logName -EventID $eventID -EntryType $level -Message $message
Write-Host $message
}
function Throw-Error($message) {
Write-EventLog -Source $eventSource -LogName $logName -EventID $eventID -EntryType Error -Message $message
throw $message
}
Write-Message "Starting auto-update workflow ..."
$ErrorActionPreference = 'Stop'
$installDir = $PSScriptRoot
if ((Test-Path HKLM:\Software\Certbot) -And ((Get-ItemProperty -Path HKLM:\Software\Certbot).PSObject.Properties.Name -Contains "CertbotSigningPubKey")) {
$certbotSigningPubKey = (Get-ItemProperty -Path HKLM:\Software\Certbot).CertbotSigningPubKey
} else {
$certbotSigningPubKey = '
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq
OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18
xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp
9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij
n9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH
cXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+
CQIDAQAB
-----END PUBLIC KEY-----
'
}
if ((Test-Path HKLM:\Software\Certbot) -And ((Get-ItemProperty -Path HKLM:\Software\Certbot).PSObject.Properties.Name -Contains "CertbotUpgradeApiURL")) {
$certbotUpgradeApiURL = (Get-ItemProperty -Path HKLM:\Software\Certbot).CertbotUpgradeApiURL
} else {
$certbotUpgradeApiURL = 'https://api.github.com/repos/certbot/certbot/releases/latest'
}
# Get current local certbot version
try {
$currentVersion = certbot --version
$currentVersion = $currentVersion -replace '^certbot (\d+\.\d+\.\d+).*$', '$1'
$currentVersion = [System.Version]"$currentVersion"
} catch {
Write-Message @"
An error occured while fetching the current local certbot version:
$_
Assuming Certbot is not up-to-date.
"@ "Warning"
$currentVersion = [System.Version]"0.0.0"
}
# Get latest remote certbot version
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$result = Invoke-RestMethod -Uri $certbotUpgradeApiURL
$latestVersion = $result.tag_name -replace '^v(\d+\.\d+\.\d+).*$', '$1'
$latestVersion = [System.Version]"$latestVersion"
} catch {
Throw-Error @"
Could not get the latest remote certbot version. Error was:
$_
Aborting auto-upgrade process.
"@
}
if ($currentVersion -ge $latestVersion) {
Write-Message "No upgrade is needed, Certbot is already at the latest version ($currentVersion)."
} else {
# Search for the Windows installer asset
$installerUrl = $null
foreach ($asset in $result.assets) {
if ($asset.name -match '^certbot-.*installer-win32\.exe$') {
$installerUrl = $asset.browser_download_url
}
}
if ($null -eq $installerUrl) {
Throw-Error "Could not find the URL for the latest Certbot for Windows installer."
}
Write-Message "Starting Certbot auto-upgrade from $currentVersion to $latestVersion ..."
$installerPath = "$env:TMP/certbot-installer-win32.exe"
try {
# Download the installer
Write-Message "Downloading the installer ..."
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($installerUrl, $installerPath)
# Check installer has a valid signature from the Certbot release team
$signature = Get-AuthenticodeSignature $installerPath
if ($signature.Status -ne 'Valid') {
throw "Downloaded installer has no or invalid Authenticode signature."
}
$publicKey = $certbotSigningPubKey -replace '-+.*-+' -replace "`n" -replace "`r"
$refBinaryPublicKey = [System.Convert]::FromBase64String($publicKey)
$curBinaryPublicKey = $signature.SignerCertificate.PublicKey.EncodedKeyValue.RawData
$diff = Compare-Object -ReferenceObject $refBinaryPublicKey -DifferenceObject $curBinaryPublicKey
if ($diff) {
throw "Downloaded installer has not been signed by Certbot development team."
}
if (Test-Path $installDir\uninstall.exe) {
# Uninstall old Certbot first
Write-Message "Running the uninstaller for old version (install dir: $installDir) ..."
Start-Process -FilePath $installDir\uninstall.exe -ArgumentList "/S" -Wait
}
# Install new version of Certbot
Write-Message "Running the installer for new version (install dir: $installDir) ..."
Start-Process -FilePath $installerPath -ArgumentList "/S /D=$installDir" -Wait
Write-Message "Certbot $latestVersion is installed."
} catch {
Throw-Error @"
Could not update to the latest remote certbot version. Error was:
$_
Aborting auto-upgrade process.
"@
} finally {
Remove-Item $installerPath -ErrorAction 'Ignore'
}
}
Write-Message "Finished auto-update workflow."
}
end {}

View File

@@ -84,8 +84,9 @@ def _copy_assets(build_path, repo_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', 'tasks-up.ps1'), build_path)
shutil.copy(os.path.join(repo_path, 'windows-installer', 'tasks-down.ps1'), build_path)
shutil.copy(os.path.join(repo_path, 'windows-installer', 'auto-update.ps1'), build_path)
def _generate_pynsist_config(repo_path, build_path):
@@ -144,8 +145,9 @@ bitness={python_bitness}
[Include]
local_wheels=wheels\\*.whl
files=run.bat
renew-up.ps1
renew-down.ps1
tasks-up.ps1
tasks-down.ps1
auto-update.ps1
[Command certbot]
entry_point=certbot.main:main

View File

@@ -1,6 +0,0 @@
$taskName = "Certbot Renew Task"
$exists = Get-ScheduledTask | Where-Object {$_.TaskName -like $taskName}
if ($exists) {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}

View File

@@ -1,17 +0,0 @@
function Get-ScriptDirectory { Split-Path $MyInvocation.ScriptName }
$down = Join-Path (Get-ScriptDirectory) 'renew-down.ps1'
& $down
$taskName = "Certbot Renew Task"
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument '-NoProfile -WindowStyle Hidden -Command "certbot renew"'
$delay = New-TimeSpan -Hours 12
$triggerAM = New-ScheduledTaskTrigger -Daily -At 12am -RandomDelay $delay
$triggerPM = New-ScheduledTaskTrigger -Daily -At 12pm -RandomDelay $delay
# NB: For now scheduled task is set up under Administrators group account because Certbot Installer installs Certbot for all users.
# If in the future we allow the Installer to install Certbot for one specific user, the scheduled task will need to
# switch to this user, since Certbot will be available only for him.
$adminsSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")
$adminsGroupID = $adminsSID.Translate([System.Security.Principal.NTAccount]).Value
$principal = New-ScheduledTaskPrincipal -GroupId $adminsGroupID -RunLevel Highest
Register-ScheduledTask -Action $action -Trigger $triggerAM,$triggerPM -TaskName $taskName -Description "Execute twice a day the 'certbot renew' command, to renew managed certificates if needed." -Principal $principal

View File

@@ -0,0 +1,10 @@
#Requires -Version 5.0
$tasks = "Certbot Renew and Auto-Update Task", "Certbot Renew Task"
foreach ($task in $tasks) {
$exists = Get-ScheduledTask | Where-Object { $_.TaskName -like $task }
if ($exists)
{
Unregister-ScheduledTask -TaskName $task -Confirm:$false
}
}

View File

@@ -0,0 +1,27 @@
#Requires -Version 5.0
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]
$InstallDir
)
function Get-ScriptDirectory { Split-Path $MyInvocation.ScriptName }
$down = Join-Path (Get-ScriptDirectory) 'tasks-down.ps1'
& $down
$taskName = "Certbot Renew and Auto-Update Task"
$taskDescription = "Execute twice a day the 'certbot renew' command, to renew managed certificates if needed, and upgrade Certbot if a new version is available."
$actionRenew = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "-NoProfile -WindowStyle Hidden -Command ""& '$InstallDir\bin\certbot.exe' renew"""
$actionUpgrade = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument "-NoProfile -WindowStyle Hidden -File ""$InstallDir\auto-update.ps1"""
$delay = New-TimeSpan -Hours 12
$triggerAM = New-ScheduledTaskTrigger -Daily -At 12am -RandomDelay $delay
$triggerPM = New-ScheduledTaskTrigger -Daily -At 12pm -RandomDelay $delay
# NB: For now scheduled task is set up under Administrators account because Certbot Installer installs Certbot for all users.
# If in the future we allow the Installer to install Certbot for one specific user, the scheduled task will need to
# switch to this user, since Certbot will be available only for him.
$adminSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")
$adminGroupID = $adminSID.Translate([System.Security.Principal.NTAccount]).Value
$principal = New-ScheduledTaskPrincipal -GroupId $adminGroupID -RunLevel Highest
Register-ScheduledTask -Action $actionRenew, $actionUpgrade -Trigger $triggerAM, $triggerPM -TaskName $taskName -Description $taskDescription -Principal $principal

View File

@@ -0,0 +1,183 @@
--- pyapp.nsi 2020-03-31 01:46:13.118535700 +0200
+++ template.nsi 2020-03-31 01:45:37.054503000 +0200
@@ -1,3 +1,13 @@
+; This NSIS template is based on the built-in one in pynsist 2.4.
+; If pynsist is upgraded, this template may be updated if necessary using the new built-in one.
+; Original file can be found here: https://github.com/takluyver/pynsist/blob/2.4/nsist/pyapp.nsi
+; Diff file is located at .\template-nsi.patch
+
+; Require the installer do be installed with admin privileges
+RequestExecutionLevel admin
+; Set default installation path (overridable with /D= flag)
+InstallDir "$PROGRAMFILES\Certbot"
+
!define PRODUCT_NAME "[[ib.appname]]"
!define PRODUCT_VERSION "[[ib.version]]"
!define PY_VERSION "[[ib.py_version]]"
@@ -6,24 +16,22 @@
!define ARCH_TAG "[[arch_tag]]"
!define INSTALLER_NAME "[[ib.installer_name]]"
!define PRODUCT_ICON "[[icon]]"
-
-; Marker file to tell the uninstaller that it's a user installation
-!define USER_INSTALL_MARKER _user_install_marker
-
+
SetCompressor lzma
-!define MULTIUSER_EXECUTIONLEVEL Highest
-!define MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
-!define MULTIUSER_MUI
-!define MULTIUSER_INSTALLMODE_COMMANDLINE
-!define MULTIUSER_INSTALLMODE_INSTDIR "[[ib.appname]]"
-[% if ib.py_bitness == 64 %]
-!define MULTIUSER_INSTALLMODE_FUNCTION correct_prog_files
-[% endif %]
-!include MultiUser.nsh
+Function .onInit
+ ; Check that Powershell is at least 5.0.
+ nsExec::ExecToStack `powershell -ExecutionPolicy RemoteSigned -Command "Write-Host -NoNewline (Get-Host | Select-Object Version).Version.CompareTo((New-Object -TypeName System.Version -ArgumentList '5.0'))"`
+ Pop $0
+ Pop $1
+ StrCmp $1 "-1" 0 powershellok
+ MessageBox MB_OK|MB_ICONSTOP "Certbot requires Powershell 5.0+ to work.$\r$\nPlease check the following link to know how to upgrade your system:$\r$\nhttps://docs.microsoft.com/powershell/scripting/install/installing-powershell"
+ Abort
+ powershellok:
+FunctionEnd
[% block modernui %]
-; Modern UI installer stuff
+; Modern UI installer stuff
!include "MUI2.nsh"
!define MUI_ABORTWARNING
!define MUI_ICON "[[icon]]"
@@ -35,7 +43,6 @@
[% if license_file %]
!insertmacro MUI_PAGE_LICENSE [[license_file]]
[% endif %]
-!insertmacro MULTIUSER_PAGE_INSTALLMODE
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
@@ -43,7 +50,7 @@
!insertmacro MUI_LANGUAGE "English"
[% endblock modernui %]
-Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
+Name "${PRODUCT_NAME} (beta) ${PRODUCT_VERSION}"
OutFile "${INSTALLER_NAME}"
ShowInstDetails show
@@ -56,18 +63,13 @@
Section "!${PRODUCT_NAME}" sec_app
SetRegView [[ib.py_bitness]]
+ SetShellVarContext all
SectionIn RO
File ${PRODUCT_ICON}
SetOutPath "$INSTDIR\pkgs"
File /r "pkgs\*.*"
SetOutPath "$INSTDIR"
- ; Marker file for per-user install
- StrCmp $MultiUser.InstallMode CurrentUser 0 +3
- FileOpen $0 "$INSTDIR\${USER_INSTALL_MARKER}" w
- FileClose $0
- SetFileAttributes "$INSTDIR\${USER_INSTALL_MARKER}" HIDDEN
-
[% block install_files %]
; Install files
[% for destination, group in grouped_files %]
@@ -76,14 +78,14 @@
File "[[ file ]]"
[% endfor %]
[% endfor %]
-
+
; Install directories
[% for dir, destination in ib.install_dirs %]
SetOutPath "[[ pjoin(destination, dir) ]]"
File /r "[[dir]]\*.*"
[% endfor %]
[% endblock install_files %]
-
+
[% block install_shortcuts %]
; Install shortcuts
; The output path becomes the working directory for shortcuts
@@ -109,17 +111,11 @@
DetailPrint "Setting up command-line launchers..."
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_assemble_launchers.py" [[ python ]] "$INSTDIR\bin"'
- StrCmp $MultiUser.InstallMode CurrentUser 0 AddSysPathSystem
- ; Add to PATH for current user
- nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add_user "$INSTDIR\bin"'
- GoTo AddedSysPath
- AddSysPathSystem:
- ; Add to PATH for all users
- nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
- AddedSysPath:
+ ; Add to PATH for all users
+ nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
[% endif %]
[% endblock install_commands %]
-
+
; Byte-compile Python files.
DetailPrint "Byte-compiling Python modules..."
nsExec::ExecToLog '[[ python ]] -m compileall -q "$INSTDIR\pkgs"'
@@ -144,6 +140,10 @@
WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"NoRepair" 1
+ ; Execute ps script to create the certbot renew & auto-update task
+ DetailPrint "Setting up certbot renew & auto-update scheduled task"
+ nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-up.ps1" -InstallDir "$INSTDIR"'
+
; Check if we need to reboot
IfRebootFlag 0 noreboot
MessageBox MB_YESNO "A reboot is required to finish the installation. Do you wish to reboot now?" \
@@ -155,11 +155,14 @@
Section "Uninstall"
SetRegView [[ib.py_bitness]]
SetShellVarContext all
- IfFileExists "$INSTDIR\${USER_INSTALL_MARKER}" 0 +3
- SetShellVarContext current
- Delete "$INSTDIR\${USER_INSTALL_MARKER}"
- Delete $INSTDIR\uninstall.exe
+ ; Execute ps script to remove the certbot renew & auto-update task, then delete scripts
+ nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-down.ps1"'
+ Delete "$INSTDIR\tasks-down.ps1"
+ Delete "$INSTDIR\tasks-up.ps1"
+ Delete "$INSTDIR\auto-update.ps1"
+
+ Delete "$INSTDIR\uninstall.exe"
Delete "$INSTDIR\${PRODUCT_ICON}"
RMDir /r "$INSTDIR\pkgs"
@@ -207,23 +210,6 @@
[% block mouseover_messages %]
StrCmp $0 ${sec_app} "" +2
SendMessage $R0 ${WM_SETTEXT} 0 "STR:${PRODUCT_NAME}"
-
+
[% endblock mouseover_messages %]
FunctionEnd
-
-Function .onInit
- !insertmacro MULTIUSER_INIT
-FunctionEnd
-
-Function un.onInit
- !insertmacro MULTIUSER_UNINIT
-FunctionEnd
-
-[% if ib.py_bitness == 64 %]
-Function correct_prog_files
- ; The multiuser machinery doesn't know about the different Program files
- ; folder for 64-bit applications. Override the install dir it set.
- StrCmp $MultiUser.InstallMode AllUsers 0 +2
- StrCpy $INSTDIR "$PROGRAMFILES64\${MULTIUSER_INSTALLMODE_INSTDIR}"
-FunctionEnd
-[% endif %]

View File

@@ -1,7 +1,12 @@
; This NSIS template is based on the built-in one in pynsist 2.3.
; Added lines are enclosed within "CERTBOT CUSTOM BEGIN/END" comments.
; If pynsist is upgraded, this template must be updated if necessary using the new built-in one.
; This NSIS template is based on the built-in one in pynsist 2.4.
; If pynsist is upgraded, this template may be updated if necessary using the new built-in one.
; Original file can be found here: https://github.com/takluyver/pynsist/blob/2.4/nsist/pyapp.nsi
; Diff file is located at .\template-nsi.patch
; Require the installer do be installed with admin privileges
RequestExecutionLevel admin
; Set default installation path (overridable with /D= flag)
InstallDir "$PROGRAMFILES\Certbot"
!define PRODUCT_NAME "[[ib.appname]]"
!define PRODUCT_VERSION "[[ib.version]]"
@@ -11,30 +16,19 @@
!define ARCH_TAG "[[arch_tag]]"
!define INSTALLER_NAME "[[ib.installer_name]]"
!define PRODUCT_ICON "[[icon]]"
; Marker file to tell the uninstaller that it's a user installation
!define USER_INSTALL_MARKER _user_install_marker
SetCompressor lzma
; CERTBOT CUSTOM BEGIN
; Administrator privileges are required to insert a new task in Windows Scheduler.
; Also comment out some options to disable ability to choose AllUsers/CurrentUser install mode.
; As a result, installer run always with admin privileges (because of MULTIUSER_EXECUTIONLEVEL),
; using the AllUsers installation mode by default (because of MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
; not set), and this default behavior cannot be overridden (because of MULTIUSER_MUI not set).
; See https://nsis.sourceforge.io/Docs/MultiUser/Readme.html
!define MULTIUSER_EXECUTIONLEVEL Admin
;!define MULTIUSER_EXECUTIONLEVEL Highest
;!define MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
;!define MULTIUSER_MUI
;!define MULTIUSER_INSTALLMODE_COMMANDLINE
; CERTBOT CUSTOM END
!define MULTIUSER_INSTALLMODE_INSTDIR "[[ib.appname]]"
[% if ib.py_bitness == 64 %]
!define MULTIUSER_INSTALLMODE_FUNCTION correct_prog_files
[% endif %]
!include MultiUser.nsh
Function .onInit
; Check that Powershell is at least 5.0.
nsExec::ExecToStack `powershell -ExecutionPolicy RemoteSigned -Command "Write-Host -NoNewline (Get-Host | Select-Object Version).Version.CompareTo([System.Version]'5.0')"`
Pop $0
Pop $1
StrCmp $1 "-1" 0 powershellok
MessageBox MB_OK|MB_ICONSTOP "Certbot requires Powershell 5.0+ to work.$\r$\nPlease check the following link to know how to upgrade your system:$\r$\nhttps://docs.microsoft.com/powershell/scripting/install/installing-powershell"
Abort
powershellok:
FunctionEnd
[% block modernui %]
; Modern UI installer stuff
@@ -49,10 +43,6 @@ SetCompressor lzma
[% if license_file %]
!insertmacro MUI_PAGE_LICENSE [[license_file]]
[% endif %]
; CERTBOT CUSTOM BEGIN
; Disable the installation mode page (AllUsers/CurrentUser)
;!insertmacro MULTIUSER_PAGE_INSTALLMODE
; CERTBOT CUSTOM END
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
@@ -60,10 +50,7 @@ SetCompressor lzma
!insertmacro MUI_LANGUAGE "English"
[% endblock modernui %]
; CERTBOT CUSTOM BEGIN
Name "${PRODUCT_NAME} (beta) ${PRODUCT_VERSION}"
;Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
; CERTBOT CUSTOM END
OutFile "${INSTALLER_NAME}"
ShowInstDetails show
@@ -76,18 +63,13 @@ SectionEnd
Section "!${PRODUCT_NAME}" sec_app
SetRegView [[ib.py_bitness]]
SetShellVarContext all
SectionIn RO
File ${PRODUCT_ICON}
SetOutPath "$INSTDIR\pkgs"
File /r "pkgs\*.*"
SetOutPath "$INSTDIR"
; Marker file for per-user install
StrCmp $MultiUser.InstallMode CurrentUser 0 +3
FileOpen $0 "$INSTDIR\${USER_INSTALL_MARKER}" w
FileClose $0
SetFileAttributes "$INSTDIR\${USER_INSTALL_MARKER}" HIDDEN
[% block install_files %]
; Install files
[% for destination, group in grouped_files %]
@@ -129,14 +111,8 @@ Section "!${PRODUCT_NAME}" sec_app
DetailPrint "Setting up command-line launchers..."
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_assemble_launchers.py" [[ python ]] "$INSTDIR\bin"'
StrCmp $MultiUser.InstallMode CurrentUser 0 AddSysPathSystem
; Add to PATH for current user
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add_user "$INSTDIR\bin"'
GoTo AddedSysPath
AddSysPathSystem:
; Add to PATH for all users
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
AddedSysPath:
; Add to PATH for all users
nsExec::ExecToLog '[[ python ]] -Es "$INSTDIR\_system_path.py" add "$INSTDIR\bin"'
[% endif %]
[% endblock install_commands %]
@@ -164,11 +140,9 @@ Section "!${PRODUCT_NAME}" sec_app
WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"NoRepair" 1
; CERTBOT CUSTOM BEGIN
; Execute ps script to create the certbot renew task
DetailPrint "Setting up certbot renew scheduled task"
nsExec::ExecToStack 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\renew-up.ps1"'
; CERTBOT CUSTOM END
; Execute ps script to create the certbot renew & auto-update task
DetailPrint "Setting up certbot renew & auto-update scheduled task"
nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-up.ps1" -InstallDir "$INSTDIR"'
; Check if we need to reboot
IfRebootFlag 0 noreboot
@@ -179,18 +153,16 @@ Section "!${PRODUCT_NAME}" sec_app
SectionEnd
Section "Uninstall"
; CERTBOT CUSTOM BEGIN
; Execute ps script to remove the certbot renew task
nsExec::ExecToStack 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\renew-down.ps1"'
; CERTBOT CUSTOM END
SetRegView [[ib.py_bitness]]
SetShellVarContext all
IfFileExists "$INSTDIR\${USER_INSTALL_MARKER}" 0 +3
SetShellVarContext current
Delete "$INSTDIR\${USER_INSTALL_MARKER}"
Delete $INSTDIR\uninstall.exe
; Execute ps script to remove the certbot renew & auto-update task, then delete scripts
nsExec::ExecToLog 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$INSTDIR\tasks-down.ps1"'
Delete "$INSTDIR\tasks-down.ps1"
Delete "$INSTDIR\tasks-up.ps1"
Delete "$INSTDIR\auto-update.ps1"
Delete "$INSTDIR\uninstall.exe"
Delete "$INSTDIR\${PRODUCT_ICON}"
RMDir /r "$INSTDIR\pkgs"
@@ -241,20 +213,3 @@ Function .onMouseOverSection
[% endblock mouseover_messages %]
FunctionEnd
Function .onInit
!insertmacro MULTIUSER_INIT
FunctionEnd
Function un.onInit
!insertmacro MULTIUSER_UNINIT
FunctionEnd
[% if ib.py_bitness == 64 %]
Function correct_prog_files
; The multiuser machinery doesn't know about the different Program files
; folder for 64-bit applications. Override the install dir it set.
StrCmp $MultiUser.InstallMode AllUsers 0 +2
StrCpy $INSTDIR "$PROGRAMFILES64\${MULTIUSER_INSTALLMODE_INSTDIR}"
FunctionEnd
[% endif %]