Crie um instalador de aplicativo da Web em Python, incluindo Apache, Django e PostgreSQL para Windows





Este post é uma continuação da primeira parte do artigo sobre Habré , onde foi descrito em detalhes sobre como implantar a pilha do Django no MS Windows. Em seguida, forneceremos instruções passo a passo para a criação de um instalador que automatizará o processo de instalação da pilha em outros computadores sem a necessidade de trabalhar na linha de comando, criando máquinas virtuais, etc., onde toda a sequência de ações será reduzida às ações Próximo -> Próximo -> Concluir.



Então, o que o instalador deve fazer:



  1. Descompacte todos os programas e componentes necessários em um diretório especificado pelo usuário.
  2. Execute verificações de pré-instalação.
  3. Registre o interpretador Python no registro do Windows.
  4. Instale, se ainda não estiver instalado, as bibliotecas de dependência de software.
  5. Crie serviços Apache e PostgreSQL e inicie-os.
  6. Uma vantagem adicional será a criação automática de um programa desinstalador que removerá a pilha instalada se o usuário quiser.


Dentre as opções possíveis para instaladores, iremos escolher o instalador gratuito Inno Setup. ele permite que você faça todos os itens acima, permitindo que você crie instaladores sem ter que executar muitos scripts. Comparado ao Wix, a sintaxe do arquivo de configuração é o formato ini, que é mais fácil de ler e alterar do que xml. Hoje, ele compete e até supera muitos instaladores comerciais em conjunto de recursos e estabilidade.



Melhor de tudo, nenhum script é necessário para criar um instalador básico, já que o Inno Setup vem com um assistente gráfico que faz um trabalho surpreendentemente bom com os instaladores básicos.



A lógica de instalação pode ser escrita na linguagem Pascal em vez de ações personalizadas complicadas no Wix. Sua única desvantagem é que ele cria apenas exe, o formato de arquivo msi não é compatível.



Etapa 1.Inno Setup Installation



Comentários adicionais não são necessários aqui, uma vez que baixar e instalar o programa de instalação é trivial.



Etapa 2: Script de instalação do Inno Setup



Vamos criar um script de stub do Inno Setup (arquivo * .iss) usando o Installation Script Wizard.



















































Como resultado, um arquivo * .iss será criado com o seguinte conteúdo:
; 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







Etapa 3. Verificações antes da instalação



Antes de descompactar os programas para o diretório e alterar o registro, você precisa verificar se as portas TCP estão livres para o Apache e PostgreSQL funcionarem, você também precisa verificar os requisitos mínimos de sistema do sistema operacional Windows, uma vez que conforme já mencionado na primeira parte deste artigo, a versão instalada do Python funcionará apenas a partir da versão do MS Windows 8 (kernel versão 6.2) .



Para realizar as verificações necessárias, vamos usar a seção [Código] do arquivo de instalação. Seção [Código]É uma seção opcional que define um script Pascal. O script Pascal pode ser usado para personalizar a instalação ou desinstalação de diferentes maneiras. Observe que criar um script Pascal não é fácil e requer experiência com Inno Setup e habilidades de programação em Pascal, ou pelo menos uma linguagem de programação semelhante. Para verificar a disponibilidade das portas TCP, criaremos a seguinte função: Chamaremos as funções de teste na função InitializeSetup , que é chamada durante a inicialização da instalação. Retorna False para cancelar a instalação, caso contrário, 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;



Etapa 4. Registre o Python no registro do Windows



Esta seção opcional define quaisquer chaves / valores de registro que o instalador deve criar ou modificar no sistema do usuário.



Para fazer isso, adicione as PythonPath e chaves PYTHONHOME e atualizar o Path variável .



sys.path contém uma lista de strings que fornecem locais de pesquisa para módulos e pacotes para um futuro projeto Python. Ele é inicializado a partir da variável de ambiente PYTHONPATH e outras configurações.



PYTHONHOME é o diretório inicial do Python.



PATH é uma variável de ambiente que o sistema operacional usa para localizar executáveis ​​na linha de comando ou na janela do 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"



Etapa 5. Criar arquivos de configuração para serviços Apache e PostgreSQL



Para criar arquivos de configuração, usaremos 2 scripts Python que irão gerar a configuração com base no caminho de instalação especificado pelo usuário.



Os scripts serão chamados na seção [Executar] do instalador.



A seção [Executar] é opcional e especifica qualquer número de programas a serem executados após a instalação bem-sucedida do programa, mas antes que o instalador exiba a caixa de diálogo final.



Em seguida, na mesma seção, adicione a instalação oculta dos Pacotes Redistribuíveis do Visual Studio, sem os quais os serviços Apache e PostgreSQL não funcionarão. Conteúdo do arquivo 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)



Conteúdo 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)


Arquivo Install.bat Conteúdo arquivo 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"



Etapa 6: crie o desinstalador



Para qualquer instalador, também é necessário prever a possibilidade de criação de um programa desinstalador. Felizmente, o Inno Setup fará o trabalho para nós, exceto por algumas etapas que precisam ser executadas para limpar rastros da presença do programa no sistema operacional.



Para isso, na seção [UninstallRun] , registraremos a execução do script bat do Windows para interromper os serviços instalados, bem como para removê-los. O conteúdo do script bat: O script para os serviços e, em seguida, remove os serviços Apache e PostgreSQL da lista de serviços do sistema Windows.



[UninstallRun]

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







@echo off



SC STOP Apache

SC STOP PostgreSQL



SC DELETE Apache

SC DELETE PostgreSQL







Etapa 7. Assinatura do arquivo executável do instalador ES do desenvolvedor



Os certificados de assinatura de código são usados ​​por desenvolvedores de software para assinar digitalmente aplicativos e programas para provar que um arquivo carregado por um usuário é genuíno e não foi adulterado. Isso é especialmente importante para editores que distribuem seu software por meio de sites de download de terceiros sobre os quais eles não têm controle. Os principais sistemas operacionais mostrarão aos usuários finais uma mensagem de erro se o software que eles estão tentando instalar não for assinado por uma autoridade de certificação confiável.



Você pode comprar um certificado de desenvolvedor PFX, por exemplo, aqui . O certificado é adquirido por um ano.



A penúltima etapa para trabalhar com o instalador será iniciar automaticamente o programa signtool.exe para assinar o instalador concluído em formato exe depois que o programa Inno Setup completar seu trabalho. SignTool é um programa de linha de comando que assina arquivos digitalmente, verifica assinaturas de arquivos e carimbos de data / hora. Por padrão, o programa signtool.exe não está incluído na distribuição do Windows, portanto, baixamos e instalamos o Windows 10 SDK .



Após a conclusão da instalação, você encontrará signtool.exe nos diretórios:



  • x86 -> c: \ Arquivos de programas (x86) \ Windows Kits \ 10 \ bin \ x86 \
  • x64 -> c: \ Arquivos de programas (x86) \ Windows Kits \ 10 \ bin \ x64 \


Para quem deseja se familiarizar com o programa de assinaturas, visite o site oficial do desenvolvedor para obter mais detalhes . Ele lista todas as opções de linha de comando e exemplos de uso. Vamos continuar.



A seguir, vamos configurar a assinatura automática do arquivo. Selecione "Configurar Ferramentas de Sinal ..." no menu "Ferramentas" .







Em seguida, clique no botão "Adicionar" e







dê um nome à ferramenta. Este é o nome que você usará ao se referir à ferramenta nos scripts do instalador. Nomeei meu signtool porque estou usando signtool.exe.







Cole o texto que você usa para assinar arquivos executáveis ​​na linha de comando. Substitua o nome do arquivo a ser assinado por $ f. O Inno Setup substituirá a variável $ f pelo arquivo assinado.



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







Após pressionar OK, você concluiu a configuração da ferramenta de assinatura.







Vamos adicionar o seguinte script à seção [Setup] para usar a ferramenta de assinatura que acabamos de configurar. Isso pressupõe que você nomeou sua ferramenta de sinalização.



SignTool=signtool



Etapa 8. Montagem do instalador



Arquivo do instalador do InnoSetup final
#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;











Etapa 9. Verificando o trabalho do instalador







































Isso é tudo, obrigado pela atenção.



All Articles