Cree un instalador de aplicaciones web Python que incluya Apache, Django y PostgreSQL para Windows





Esta publicación es una continuación de la primera parte del artículo sobre Habré , donde se describió en detalle sobre la implementación de la pila Django en MS Windows. A continuación, proporcionaremos instrucciones paso a paso para crear un instalador que automatizará el proceso de instalación de la pila en otros equipos sin necesidad de trabajar en la línea de comandos, creando máquinas virtuales, etc., donde toda la secuencia de acciones se reducirá a las acciones Siguiente -> Siguiente -> Finalizar.



Entonces, qué debe hacer el instalador:



  1. Desempaquete todos los programas y componentes necesarios en un directorio especificado por el usuario.
  2. Realice las comprobaciones previas a la instalación.
  3. Registre el intérprete de Python en el registro de Windows.
  4. Instale, si aún no está instalado, las bibliotecas de dependencia de software.
  5. Cree servicios Apache y PostgreSQL, luego inícielos.
  6. Una ventaja adicional será la creación automática de un programa de desinstalación que eliminará la pila instalada si el usuario lo desea.


Entre las posibles opciones para los instaladores, elegiremos el instalador gratuito Inno Setup, porque le permite hacer todo lo anterior, lo que le permite crear instaladores sin tener que ejecutar muchos scripts. En comparación con Wix, la sintaxis del archivo de configuración es el formato ini, que es más fácil de leer y cambiar que XML. Hoy compite con muchos instaladores comerciales e incluso los supera en conjunto de características y estabilidad.



Lo mejor de todo es que no se requiere ningún script para crear un instalador básico, ya que Inno Setup viene con un asistente gráfico que hace un trabajo sorprendentemente bueno con los instaladores básicos.



La lógica de instalación se puede escribir en lenguaje Pascal en lugar de complicadas acciones personalizadas en Wix. Su único inconveniente es que solo crea exe, el formato de archivo msi no es compatible.



Paso 1: instalación de Inno Setup



No se necesitan comentarios adicionales aquí, ya que descargar e instalar el programa de instalación es trivial.



Paso 2: Script de la instalación de Inno Setup



Creemos una secuencia de comandos de Stub Inno Setup (archivo * .iss) usando el Asistente de secuencia de comandos de instalación.



















































Como resultado, se creará un archivo * .iss con el siguiente contenido:
; Script generated by the Inno Setup Script Wizard.

; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!



#define MyAppName "Severcart"

#define MyAppVersion "1.21.0"

#define MyAppPublisher "Severcart Inc."

#define MyAppURL "https://www.severcart.ru/"



[Setup]

; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.

; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)

AppId={{4FAF87DC-4DBD-42CE-A2A2-B6D559E76BDC}

AppName={#MyAppName}

AppVersion={#MyAppVersion}

;AppVerName={#MyAppName} {#MyAppVersion}

AppPublisher={#MyAppPublisher}

AppPublisherURL={#MyAppURL}

AppSupportURL={#MyAppURL}

AppUpdatesURL={#MyAppURL}

DefaultDirName=c:\severcart

DefaultGroupName={#MyAppName}

; Uncomment the following line to run in non administrative install mode (install for current user only.)

;PrivilegesRequired=lowest

OutputDir=C:\Users\Developer\Desktop\Output

OutputBaseFilename=mysetup

Compression=lzma

SolidCompression=yes

WizardStyle=modern



[Languages]

Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl"



[Files]

Source: "C:\severcart\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

; NOTE: Don't use "Flags: ignoreversion" on any shared system files







Paso 3. Verificaciones antes de la instalación



Antes de desempaquetar programas en el directorio y cambiar el registro, debe verificar que los puertos TCP estén libres para que funcione Apache y PostgreSQL, también debe verificar los requisitos mínimos del sistema de Windows, porque Como ya se mencionó en la primera parte de este artículo, la versión instalada de Python funcionará solo a partir de la versión de MS Windows 8 (versión del kernel 6.2) .



Para realizar las comprobaciones necesarias, usaremos la sección [Código] del archivo de instalación. Sección [Código]Es una sección opcional que define un script Pascal. El script Pascal se puede utilizar para personalizar la instalación o desinstalación de diferentes formas. Tenga en cuenta que crear un script Pascal no es fácil y requiere experiencia con Inno Setup y habilidades de programación en Pascal, o al menos un lenguaje de programación similar. Para comprobar la disponibilidad de los puertos TCP, crearemos la siguiente función: Llamaremos a las funciones de prueba en la función InitializeSetup , que se llama durante la inicialización de la instalación. Devuelve False para cancelar la instalación; de lo contrario, True .



function IsWindowsVersionOrNewer(Major, Minor: Integer): Boolean;

var

Version: TWindowsVersion;

begin

GetWindowsVersionEx(Version);

Result := (Version.Major > Major) or ((Version.Major = Major) and (Version.Minor >= Minor));

end;



function IsWindows8OrNewer: Boolean;

begin

Result := IsWindowsVersionOrNewer(6, 2);

end;







function CheckPortOccupied(Port:String):Boolean;

var

ResultCode: Integer;

begin

Exec(ExpandConstant('{cmd}'), '/C netstat -na | findstr'+' /C:":'+Port+' "', '',0,ewWaitUntilTerminated, ResultCode);

if ResultCode <> 1 then

begin

Log('this port('+Port+') is occupied');

Result := True;

end else

begin

Result := False;

end;

end;







function InitializeSetup(): Boolean;

var

port_80_check, port_5432_check: boolean;

begin

if not IsWindows8OrNewer() then begin

MsgBox(' . Windows 2012 Windows 8.0.',mbError,MB_OK);

Abort();

Result := False;

end;



port_80_check := CheckPortOccupied('8080');

if port_80_check then begin

MsgBox(' . TCP 8080 .',mbError,MB_OK);

Abort();

Result := False;

end;



port_5432_check := CheckPortOccupied('5432');

if port_5432_check then begin

MsgBox(' . TCP 5432 .',mbError,MB_OK);

Result := False;

Abort();

end;

Result := True;



Paso 4. Registre Python en el registro de Windows



Esta sección opcional define las claves / valores de registro que el instalador debe crear o modificar en el sistema del usuario.



Para hacer esto, agregue las claves PYTHONPATH y PYTHONHOME y actualice la variable Path .



sys.path contiene una lista de cadenas que proporcionan ubicaciones de búsqueda de módulos y paquetes para un futuro proyecto de Python. Se inicializa desde la variable de entorno PYTHONPATH y otras configuraciones.



PYTHONHOME es el directorio de inicio de Python.



PATH es una variable de entorno que utiliza el sistema operativo para encontrar ejecutables en la línea de comandos o la ventana de terminal.



[Registry]



Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \

ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\python;{app}\python\Scripts"



Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \

ValueType: expandsz; ValueName: "PYTHONPATH"; ValueData: "{app}\python"



Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \

ValueType: expandsz; ValueName: "PYTHONHOME"; ValueData: "{app}\python"



Paso 5. Cree archivos de configuración para los servicios Apache y PostgreSQL



Para crear archivos de configuración, usaremos 2 scripts de Python que generarán la configuración según la ruta de instalación especificada por el usuario.



Los scripts se llamarán en la sección [Ejecutar] del instalador.



La sección [Ejecutar] es opcional y especifica cualquier número de programas que se ejecutarán después de instalar correctamente el programa, pero antes de que el instalador muestre el último cuadro de diálogo.



A continuación, en la misma sección, agregue la instalación oculta de los paquetes redistribuibles de Visual Studio sin los cuales los servicios Apache y PostgreSQL no funcionarán. Contenido del archivo create_http_conf.py



[Run]



Filename: "{app}\common\VC_redist.x86apache.exe"; Parameters: "/install /passive"; Flags: waituntilterminated

Filename: "{app}\common\vcredist_x86pg.exe"; Parameters: "/install /passive"; Flags: runhidden;

Filename: "{app}\python\python.exe" ;Parameters: "{app}\common\create_http_conf.py"; Flags: runhidden

Filename: "{app}\python\python.exe" ;Parameters: "{app}\common\edit_pg_conf.py"; Flags: runhidden

Filename: "{app}\common\install.bat";Flags: runhidden

Filename: "{app}\common\services_start.bat"; Flags: runhidden







#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import sys, os


base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
base_path_un = base_path.replace('\\', '/')
apache_conf_path = os.path.join(base_path, 'Apache24', 'conf', 'extra', 'httpd-wsgi.conf')

print('base_path=',base_path)

CONF = """

LoadFile "%(base)s/python/python39.dll"
LoadModule wsgi_module "%(base)s/python/lib/site-packages/mod_wsgi/server/mod_wsgi.cp39-win32.pyd"
WSGIPythonHome "%(base)s/python"


Alias /static "%(base)s/app/static"

Alias /media "%(base)s/app/media"

<Directory "%(base)s/app/static">
    # for Apache 2.4
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

<Directory "%(base)s/app/media">
    # for Apache 2.4
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>


WSGIScriptAlias / "%(base)s/app/conf/wsgi_prod.py"
WSGIPythonPath "%(base)s/python/"

<Directory "%(base)s/app/conf/">
<Files wsgi_prod.py>
    Require all granted
</Files>   
</Directory>

"""
conf_content = CONF % {'base': base_path_un}

with open(apache_conf_path, 'w') as fp:
    fp.write(conf_content)


# Read in the file
apache_main = os.path.join(base_path, 'Apache24', 'conf', 'httpd.conf')
with open(apache_main, 'r') as file :
	filedata = file.read()

# Replace the target string
replace_pattern = 'Define SRVROOT "%(base)s/Apache24"' % {'base' : base_path_un}
find_pattern = 'Define SRVROOT "C:/severcart/Apache24"'

filedata = filedata.replace(find_pattern, replace_pattern)

# Write the file out again
with open(apache_main, 'w') as file:
	file.write(filedata)



Contenido edit_pg_conf.py



#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import sys, os

"""
c:/djangostack/postgresql/bin/postgres.exe "-D" "c:\djangostack\postgresql\data"
"""


base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
base_path_un = base_path.replace('\\', '/')
pg_conf_path = os.path.join(base_path, 'postgresql', 'data', 'postmaster.opts')


# Read in the file
pg_conf_path = os.path.join(base_path, 'postgresql', 'data', 'postmaster.opts')
with open(pg_conf_path, 'r') as file :
	filedata = file.read()


# Replace the target string
replace_pattern = base_path_un + '/'
find_pattern = "C:/severcart/"


filedata = filedata.replace(find_pattern, replace_pattern)

# Write the file out again
with open(pg_conf_path, 'w') as file:
	file.write(filedata)


Archivo Install.bat Contenido archivo services_start.bat



@echo off



..\Apache24\bin\httpd.exe -k install -n "Apache" > install.log 2>&1



..\postgresql\bin\pg_ctl.exe register -N "PostgreSQL" -D ..\postgresql\data > install.log 2>&1







@echo off



net start "Apache"



net start "PostgreSQL"



Paso 6: crea el desinstalador



Para cualquier instalador, también es necesario prever la posibilidad de crear un programa de desinstalación. Afortunadamente, Inno Setup hará el trabajo por nosotros, excepto por algunos pasos que deben tomarse para limpiar los rastros de la presencia del programa en el sistema operativo.



Para ello, en el apartado [UninstallRun] registraremos la ejecución del script bat de Windows para detener los servicios instalados, así como para eliminarlos. El contenido del script bat: el script detiene los servicios y luego elimina los servicios Apache y PostgreSQL de la lista de servicios del sistema de Windows.



[UninstallRun]

Filename: "{app}\common\remove.bat"; Flags: runhidden







@echo off



SC STOP Apache

SC STOP PostgreSQL



SC DELETE Apache

SC DELETE PostgreSQL







Paso 7. Firma del archivo ejecutable del instalador ES del desarrollador



Los desarrolladores de software utilizan los certificados de firma de código para firmar digitalmente aplicaciones y programas para demostrar que un archivo subido por un usuario es genuino y no ha sido manipulado. Esto es especialmente importante para los editores que distribuyen su software a través de sitios de descarga de terceros sobre los que no tienen control. Los principales sistemas operativos mostrarán a los usuarios finales un mensaje de error si el software que están intentando instalar no está firmado por una autoridad de certificación confiable.



Puede comprar un certificado de desarrollador PFX, por ejemplo, aquí . El certificado se compra por un año.



El penúltimo paso para trabajar con el instalador será iniciar automáticamente el programa signtool.exe para firmar el instalador terminado en formato exe después de que el programa Inno Setup complete su trabajo. SignTool es un programa de línea de comandos que firma archivos digitalmente, verifica firmas de archivos y marcas de tiempo de archivos. De forma predeterminada, el programa signtool.exe no está incluido en la distribución de Windows, por lo que descargamos e instalamos el SDK de Windows 10 .



Una vez completada la instalación, encontrará signtool.exe en los directorios:



  • x86 -> c: \ Archivos de programa (x86) \ Windows Kits \ 10 \ bin \ x86 \
  • x64 -> c: \ Archivos de programa (x86) \ Windows Kits \ 10 \ bin \ x64 \


Para aquellos que quieran familiarizarse con el programa de firma, visite el sitio web oficial del desarrollador para obtener más detalles . Enumera todas las opciones de la línea de comandos y ejemplos de uso. Vamonos.



A continuación, configuremos la firma automática del archivo. Seleccione "Configurar herramientas de señalización ..." en el menú "Herramientas" .







Luego haga clic en el botón "Agregar" y







asigne un nombre a la herramienta. Este es el nombre que utilizará cuando haga referencia a la herramienta en los scripts del instalador. Llamé al mío signtool porque estoy usando signtool.exe.







Pegue el texto que usa para firmar archivos ejecutables desde la línea de comando. Reemplace el nombre del archivo que se firmará con $ f. Inno Setup reemplazará la variable $ f con el archivo firmado.



"C: \ Archivos de programa (x86) \ Windows Kits \ 10 \ bin \ x86 \ signtool.exe" sign / f "C: \ MY_CODE_SIGNING.PFX" / t timestamp.comodoca.com/authenticode / p MY_PASSWORD $ f







Después de presionar De acuerdo, ha terminado de configurar la herramienta de firma.







Agreguemos el siguiente script a la sección [Configuración] para usar la herramienta de firma que acabamos de configurar. Esto supone que nombró a su herramienta signtool.



SignTool=signtool



Paso 8. Montaje del instalador



Archivo instalador final de InnoSetup
#define MyAppName «Severcart»

#define MyAppVersion «1.21.0»

#define MyAppPublisher «Severcart Inc.»

#define MyAppURL «www.severcart.ru»



[Setup]

; NOTE: The value of AppId uniquely identifies this application.

; Do not use the same AppId value in installers for other applications.

; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)

SignTool=signtool

AppId={{2CF113D5-B49D-47EF-B85F-AE06EB0E78EB}}

AppName={#MyAppName}

AppVersion={#MyAppVersion}

;AppVerName={#MyAppName} {#MyAppVersion}

AppPublisher={#MyAppPublisher}

AppPublisherURL={#MyAppURL}

AppSupportURL={#MyAppURL}

AppUpdatesURL={#MyAppURL}

DefaultDirName=c:\severcart

DefaultGroupName={#MyAppName}

OutputBaseFilename=setup

Compression=lzma

SolidCompression=yes

ChangesEnvironment=yes



; Uninstall options

Uninstallable=yes

CreateUninstallRegKey=yes

;WizardSmallImageFile=logo3.bmp



[Icons]

Name: "{userdesktop}\severcart"; Filename: «127.0.0.1:8080/»



[Languages]

Name: «russian»; MessagesFile: «compiler:Languages\Russian.isl»



[Files]

Source: «C:\severcart\*»; Excludes: "*.pyc"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs



[Registry]

Root: HKLM; Subkey: «SYSTEM\CurrentControlSet\Control\Session Manager\Environment»; \

ValueType: expandsz; ValueName: «Path»; ValueData: "{olddata};{app}\python;{app}\python\Scripts"



Root: HKLM; Subkey: «SYSTEM\CurrentControlSet\Control\Session Manager\Environment»; \

ValueType: expandsz; ValueName: «PYTHONPATH»; ValueData: "{app}\python"



Root: HKLM; Subkey: «SYSTEM\CurrentControlSet\Control\Session Manager\Environment»; \

ValueType: expandsz; ValueName: «PYTHONHOME»; ValueData: "{app}\python"



[Run]

Filename: "{app}\common\VC_redist.x86apache"; Parameters: "/install /passive"; Flags: waituntilterminated

Filename: "{app}\common\vcredist_x86pg"; Parameters: "/install /passive"; Flags: runhidden;

Filename: "{app}\python\python.exe" ;Parameters: "{app}\common\create_http_conf.py"; Flags: runhidden

Filename: "{app}\python\python.exe" ;Parameters: "{app}\common\edit_pg_conf.py"; Flags: runhidden

Filename: "{app}\common\install.bat";Flags: runhidden

Filename: "{app}\common\services_start.bat"; Flags: runhidden





[UninstallRun]

Filename: "{app}\common\remove.bat"; Flags: runhidden



[Code]

function IsWindowsVersionOrNewer(Major, Minor: Integer): Boolean;

var

Version: TWindowsVersion;

begin

GetWindowsVersionEx(Version);

Result :=

(Version.Major > Major) or

((Version.Major = Major) and (Version.Minor >= Minor));

end;



function IsWindows8OrNewer: Boolean;

begin

Result := IsWindowsVersionOrNewer(6, 2);

end;



function CheckPortOccupied(Port:String):Boolean;

var

ResultCode: Integer;

begin

Exec(ExpandConstant('{cmd}'), '/C netstat -na | findstr'+' /C:":'+Port+' "', '',0,ewWaitUntilTerminated, ResultCode);

if ResultCode <> 1 then

begin

Log('this port('+Port+') is occupied');

Result := True;

end else

begin

Result := False;

end;

end;



function InitializeSetup(): Boolean;

var

port_80_check, port_5432_check: boolean;

begin

if not IsWindows8OrNewer() then begin

MsgBox(' . Windows 2012 Windows 8.0.',mbError,MB_OK);

Abort();

Result := False;

end;



port_80_check := CheckPortOccupied('8080');

if port_80_check then begin

MsgBox(' . TCP 8080 .',mbError,MB_OK);

Abort();

Result := False;

end;



port_5432_check := CheckPortOccupied('5432');

if port_5432_check then begin

MsgBox(' . TCP 5432 .',mbError,MB_OK);

Result := False;

Abort();

end;

Result := True;



end;











Paso 9. Comprobación del trabajo del instalador







































Eso es todo, gracias por su atención.



All Articles