أنشئ مُثبِّت تطبيقات ويب Python يتضمن Apache و Django و PostgreSQL لنظام التشغيل Windows





هذا المنشور هو استمرار للجزء الأول من المقال عن Habré ، حيث تم وصفه بالتفصيل حول نشر مكدس Django على MS Windows. بعد ذلك ، سوف نقدم إرشادات خطوة بخطوة لإنشاء مُثبِّت يقوم بأتمتة عملية تثبيت المكدس على أجهزة كمبيوتر أخرى دون الحاجة إلى العمل في سطر الأوامر ، وإنشاء أجهزة افتراضية ، وما إلى ذلك ، حيث سيتم تقليل تسلسل الإجراءات بالكامل إلى الإجراءات التالية -> التالي -> إنهاء.



إذن ما الذي يجب أن يفعله المثبت:



  1. فك جميع البرامج والمكونات الضرورية في دليل يحدده المستخدم.
  2. قم بإجراء فحوصات ما قبل التثبيت.
  3. قم بتسجيل مترجم Python في سجل Windows.
  4. تثبيت مكتبات تبعية البرامج ، إذا لم تكن مثبتة بالفعل.
  5. أنشئ خدمات Apache و PostgreSQL ، ثم ابدأ تشغيلهما.
  6. ستكون ميزة إضافية هي الإنشاء التلقائي لبرنامج إلغاء التثبيت الذي سيزيل المكدس المثبت إذا أراد المستخدم ذلك.


من بين الخيارات الممكنة للمثبتين ، سنختار المثبت المجاني Inno Setup ، لأن يتيح لك القيام بكل ما سبق ، مما يسمح لك بإنشاء مثبتات دون الحاجة إلى تشغيل الكثير من البرامج النصية. بالمقارنة مع Wix ، يكون بناء جملة ملف الإعداد بتنسيق ini ، وهو أسهل في القراءة والتغيير من xml. اليوم ، تتنافس مع العديد من أدوات التثبيت التجارية بل وتتفوق عليها في مجموعة الميزات والاستقرار.



والأفضل من ذلك كله ، أنه لا يلزم أي برمجة نصية على الإطلاق لإنشاء مُثبِّت أساسي ، حيث يأتي برنامج Inno Setup مع معالج رسومي يقوم بعمل جيد بشكل مدهش للمثبِّتين الأساسيين.



يمكن كتابة منطق التثبيت في باسكال بدلاً من الإجراءات المخصصة المعقدة في Wix. عيبه الوحيد هو أنه ينشئ exe فقط ، تنسيق ملف msi غير مدعوم.



الخطوة الأولى: تثبيت إعداد INNO



ليست هناك حاجة إلى تعليقات إضافية هنا ، منذ ذلك الحين إن تنزيل برنامج التثبيت وتثبيته أمر تافه.



الخطوة 2: برمجة تثبيت Inno Setup



لنقم بإنشاء نص برمجي لإعداد 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. تحقق قبل التثبيت



قبل تفريغ البرامج إلى الدليل وتغيير السجل ، تحتاج إلى التحقق من أن منافذ TCP مجانية لتشغيل Apache و PostgreSQL ، كما تحتاج إلى التحقق من الحد الأدنى لمتطلبات نظام Windows ، لأن كما ذكرنا سابقًا في الجزء الأول من هذه المقالة ، سيعمل الإصدار المثبت من Python فقط بدءًا من إصدار MS Windows 8 (إصدار kernel 6.2) .



لإجراء الفحوصات اللازمة ، دعنا نستخدم قسم [الرمز] في ملف التثبيت. قسم [كود]هو قسم اختياري يعرّف نص باسكال. يمكن استخدام سكربت باسكال لتخصيص التثبيت أو إلغاء التثبيت بطرق مختلفة. لاحظ أن إنشاء نص برمجي Pascal ليس بالأمر السهل ويتطلب خبرة في إعداد Inno ومهارات البرمجة في باسكال ، أو على الأقل لغة برمجة مماثلة. للتحقق من توفر منافذ TCP ، سننشئ الوظيفة التالية: سنقوم باستدعاء وظائف الاختبار في وظيفة InitializeSetup ، والتي يتم استدعاؤها أثناء تهيئة التثبيت. إرجاع خطأ لإلغاء التثبيت ، وإلا صحيح .



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



يحدد هذا القسم الاختياري أي مفاتيح / قيم سجل يجب على المثبت إنشاؤها أو تعديلها في نظام المستخدم.



للقيام بذلك ، أضف مفتاحي PYTHONPATH و PYTHONHOME وقم بتحديث متغير المسار . يحتوي



sys.path على قائمة بالسلاسل التي توفر مواقع بحث للوحدات النمطية والحزم لمشروع Python في المستقبل. يتم تهيئته من متغير بيئة PYTHONPATH وإعدادات أخرى.



PYTHONHOME هو دليل Python الرئيسي.



PATH هو متغير بيئة يستخدمه نظام التشغيل للعثور على الملفات التنفيذية في سطر الأوامر أو النافذة الطرفية.



[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



لإنشاء ملفات التكوين ، سنستخدم نصي برمجي Python اللذين سينشئان التكوين بناءً على مسار التثبيت المحدد من قبل المستخدم.



سيتم استدعاء البرامج النصية في قسم [Run] من المثبت.



يعد قسم [Run] اختياريًا ويحدد أي عدد من البرامج ليتم تشغيلها بعد تثبيت البرنامج بنجاح ، ولكن قبل أن يعرض المثبت مربع الحوار النهائي.



بعد ذلك ، في نفس القسم ، أضف التثبيت المخفي لـ Visual Studio Redistributable Packages التي بدونها لن تعمل خدمات Apache و PostgreSQL. محتوى ملف 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: إنشاء برنامج إلغاء التثبيت



بالنسبة لأي مُثبِّت ، من الضروري أيضًا توفير إمكانية إنشاء برنامج إلغاء التثبيت. لحسن الحظ ، سيقوم برنامج Inno Setup بالمهمة نيابةً عنا ، باستثناء بعض الخطوات التي يجب اتخاذها لتنظيف آثار وجود البرنامج في نظام التشغيل.



للقيام بذلك ، في قسم [UninstallRun] ، سنقوم بتسجيل تنفيذ البرنامج النصي Windows bat لإيقاف الخدمات المثبتة ، وكذلك لإزالتها. محتويات البرنامج النصي bat: يوقف البرنامج النصي الخدمات ، ثم يزيل خدمات Apache و PostgreSQL من قائمة خدمات نظام Windows.



[UninstallRun]

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







@echo off



SC STOP Apache

SC STOP PostgreSQL



SC DELETE Apache

SC DELETE PostgreSQL







الخطوة السابعة. توقيع الملف التنفيذي لمثبت ES الخاص بالمطور



يستخدم مطورو البرامج شهادات توقيع الكود لتوقيع التطبيقات والبرامج رقميًا لإثبات أن الملف الذي تم تحميله بواسطة المستخدم أصلي ولم يتم العبث به. هذا مهم بشكل خاص للناشرين الذين يوزعون برامجهم من خلال مواقع تنزيل تابعة لجهات خارجية لا يتحكمون فيها. ستُظهر أنظمة التشغيل الرئيسية للمستخدمين النهائيين رسالة خطأ إذا لم يكن البرنامج الذي يحاولون تثبيته موقَّعًا بواسطة مرجع مصدق موثوق به.



يمكنك شراء شهادة مطور PFX ، على سبيل المثال ، من هنا . يتم شراء الشهادة لمدة عام.



ستكون الخطوة قبل الأخيرة للعمل مع المثبت هي تشغيل برنامج Signtool.exe تلقائيًا لتوقيع المثبت النهائي بتنسيق exe بعد أن يكمل برنامج إعداد Inno عمله. SignTool هو برنامج سطر أوامر يقوم بتوقيع الملفات رقميًا والتحقق من توقيعات الملفات والطوابع الزمنية للملف. بشكل افتراضي ، لا يتم تضمين برنامج Signtool.exe في توزيع Windows ، لذا قم بتنزيل Windows 10 SDK وتثبيته .



بعد اكتمال التثبيت ، ستجد signtool.exe في الدلائل:



  • x86 -> c: \ Program Files (x86) \ Windows Kits \ 10 \ bin \ x86 \
  • x64 -> c: \ Program Files (x86) \ Windows Kits \ 10 \ bin \ x64 \


بالنسبة لأولئك الذين يرغبون في التعرف على برنامج التوقيع ، قم بزيارة الموقع الرسمي للمطور بمزيد من التفاصيل . يسرد جميع خيارات سطر الأوامر وأمثلة الاستخدام. هيا لنذهب.



بعد ذلك ، لنقم بإعداد التوقيع التلقائي للملف. حدد "تكوين أدوات تسجيل ..." من قائمة "أدوات" .







بعد ذلك ، انقر فوق الزر "إضافة"







، قم بتسمية الأداة. هذا هو الاسم الذي ستستخدمه عند الإشارة إلى الأداة في نصوص المثبت. قمت بتسمية أداة Signtool الخاصة بي لأنني أستخدم Signtool.exe.







الصق النص الذي تستخدمه لتوقيع الملفات القابلة للتنفيذ من سطر الأوامر. استبدل اسم الملف المراد توقيعه بـ $ f. سيقوم Inno Setup باستبدال المتغير $ f بالملف الموقع.



"C: \ Program Files (x86) \ Windows Kits \ 10 \ bin \ x86 \ Signtool.exe" تسجيل / 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