Apache、Django、およびPostgreSQL forWindowsを含むPythonWebアプリケーションインストーラーを構築する





この投稿は、Habréに関する記事の最初の部分の続きであり、MSWindowsでのDjangoスタックの展開について詳しく説明されています。次に、コマンドラインで作業することなく他のコンピューターにスタックをインストールするプロセスを自動化するインストーラーを作成するためのステップバイステップの手順、仮想マシンの作成などを提供します。ここで、アクションのシーケンス全体がアクションNext-> Next-> Finishに削減されます。



したがって、インストーラーは何をすべきか:



  1. 必要なすべてのプログラムとコンポーネントを、ユーザーが指定したディレクトリに解凍します。
  2. インストール前のチェックを実行します。
  3. PythonインタープリターをWindowsレジストリに登録します。
  4. まだインストールされていない場合は、ソフトウェア依存関係ライブラリをインストールします。
  5. ApacheおよびPostgreSQLサービスを作成し、それらを開始します。
  6. 追加の利点は、ユーザーが必要に応じてインストールされたスタックを削除するアンインストーラープログラムの自動作成です。


インストーラーの可能なオプションの中から、無料のインストーラーInnoSetupを選択します。上記のすべてを実行できるため、多くのスクリプトを実行しなくてもインストーラーを作成できます。Wixと比較すると、セットアップファイルの構文はini形式であり、xmlよりも読みやすく変更も簡単です。今日では、機能セットと安定性において、多くの商用インストーラーと競合し、それを上回っています。



何よりも、Inno Setupには基本的なインストーラーの驚くほど優れた仕事をするグラフィカルウィザードが付属しているため、基本的なインストーラーを作成するためにスクリプトはまったく必要ありません。



インストールロジックは、Wixの複雑なカスタムアクションではなく、Pascal言語で記述できます。その唯一の欠点は、exeを作成するだけであり、msiファイル形式はサポートされていないことです。



ステップ1.Innoセットアップのインストール



ここでは追加のコメントは必要ありません。インストーラープログラムのダウンロードとインストールは簡単です。



ステップ2:InnoSetupインストールのスクリプトを作成する



インストールスクリプトウィザードを使用して、スタブInnoセットアップスクリプト(* .issファイル)を作成しましょう。



















































その結果、次の内容で* .issファイルが作成されます。
; 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







手順3.インストール前の確認



プログラムをディレクトリに解凍してレジストリを変更する前に、ApacheとPostgreSQLが機能するためにTCPポートが空いていることを確認する必要があります。また、WindowsOSの最小システム要件も確認する必要があります。この記事の最初の部分ですでに述べたように、インストールされたバージョンのPythonは、MS Windows 8のバージョン(カーネルバージョン6.2)からのみ機能します



必要なチェックを行うために、インストールファイルの[コード]セクションを使用してみましょう[コード]セクションPascalスクリプトを定義するオプションのセクションです。Pascalスクリプトを使用して、さまざまな方法でインストールまたはアンインストールをカスタマイズできます。Pascalスクリプトの作成は簡単ではなく、Pascalまたは少なくとも同様のプログラミング言語でのInnoセットアップとプログラミングスキルの経験が必要であることに注意してください。 TCPポートの可用性を確認するために、次の関数を作成します。インストールの初期化中に呼び出されるInitializeSetup 関数テスト関数を呼び出します。インストールをキャンセルする場合はFalse返し、それ以外の場合は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;



手順4.PythonをWindowsレジストリに登録する



このオプションのセクションでは、インストーラーがユーザーのシステムで作成または変更する必要のあるレジストリキー/値を定義します。



これを行うには、追加PYTHONPATHPYTHONHOMEキーをし、更新するパス変数を



sys.pathには、将来のPythonプロジェクトのモジュールとパッケージの検索場所を提供する文字列のリストが含まれています。PYTHONPATH環境変数およびその他の設定から初期化されます。



PYTHONHOMEは、Pythonのホームディレクトリです。



PATHは、OSがコマンドラインまたはターミナルウィンドウで実行可能ファイルを検索するために使用する環境変数です。



[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"



手順5.ApacheおよびPostgreSQLサービスの構成ファイルを作成する



構成ファイルを作成するには、ユーザー指定のインストールパスに基づいて構成を生成する2つのPythonスクリプトを使用します。



スクリプトは、インストーラーの[実行]セクションで呼び出されます。[実行]



セクションはオプションであり、プログラムが正常にインストールされた後、インストーラーが最後のダイアログボックスを表示する前に実行するプログラムをいくつでも指定します。 次に、同じセクションに、ApacheおよびPostgreSQLサービスが機能しないVisualStudio再頒布可能パッケージの非表示インストールを追加します。 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)



コンテンツ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)


Install.batファイル 内容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"



ステップ6:アンローダーを作成する



どのインストーラーでも、アンローダープログラムを作成できるようにする必要があります。幸い、OS内のプログラムの存在の痕跡をクリーンアップするために実行する必要のあるいくつかの手順を除いて、InnoSetupがその役割を果たします。



これを行うには、[UninstallRun]セクションで、Windows batスクリプトの実行を登録して、インストールされているサービスを停止し、それらを削除します。 batスクリプトの内容: スクリプトはサービスを停止し、WindowsシステムサービスのリストからApacheおよびPostgreSQLサービスを削除します。



[UninstallRun]

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







@echo off



SC STOP Apache

SC STOP PostgreSQL



SC DELETE Apache

SC DELETE PostgreSQL







手順7.開発者のESインストーラーの実行可能ファイルに署名する



コード署名証明書は、ソフトウェア開発者がアプリケーションやプログラムにデジタル署名して、ユーザーがアップロードしたファイルが本物であり、改ざんされていないことを証明するために使用されます。これは、自分たちが管理できないサードパーティのダウンロードサイトを通じてソフトウェアを配布するパブリッシャーにとって特に重要です。主要なオペレーティングシステムでは、インストールしようとしているソフトウェアが信頼できる認証機関によって署名されていない場合、エンドユーザーにエラーメッセージが表示されます。



たとえば、ここでPFX開発者証明書を購入できます。証明書は1年間購入されます。



インストーラーを操作する最後から2番目の手順は、Signtool.exeプログラムを自動的に起動して、Inno Setupプログラムが作業を完了した後、完成したインストーラーにexe形式で署名することです。SignToolは、ファイルにデジタル署名し、ファイル署名とファイルタイムスタンプを検証するコマンドラインプログラムです。デフォルトでは、signtool.exeプログラムはWindowsディストリビューションに含まれていないため、Windows 10SDKをダウンロードしてインストールします



インストールが完了すると、次のディレクトリにsigntool.exeが表示されます。



  • x86-> c:\プログラムファイル(x86)\ Windowsキット\ 10 \ bin \ x86 \
  • x64-> c:\プログラムファイル(x86)\ Windowsキット\ 10 \ bin \ x64 \


署名プログラムに精通したい方は、開発者の公式ウェブサイトにアクセスして詳細を確認してください。すべてのコマンドラインオプションと使用例が一覧表示されます。次へ移りましょう。



次に、ファイルの自動署名を設定しましょう。「ツール」メニューから「サインツールの設定...」を選択します 次に、[追加]ボタンをクリックし、 ツールに名前を付けます。これは、インストーラースクリプトでツールを参照するときに使用する名前です。 signtool.exeを使用しているので、signtoolという名前を付けました。 コマンドラインから実行可能ファイルに署名するために使用するテキストを貼り付けます。署名するファイルの名前を$ fに置き換えます。 Inno Setupは、$ f変数を署名付きファイルに置き換えます。



























"C:\プログラムファイル(x86)\ Windowsキット\ 10 \ bin \ x86 \ signtool.exe" sign / f "C:\ MY_CODE_SIGNING.PFX" / t timestamp.comodoca.com/authenticode / p MY_PASSWORD $ f







を押した後これで、署名ツールの構成は完了です。







設定した署名ツールを使用するために、[セットアップ]セクションに次のスクリプトを追加しましょう。これは、ツールにsigntoolという名前を付けたことを前提としています。



SignTool=signtool



ステップ8.インストーラーの組み立て



最終的な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;











ステップ9.インストーラーの動作を確認する







































それだけです、ご清聴ありがとうございました。



All Articles