PDA

View Full Version : مقاله: Installer با امکانات عالی و opensource



FastCode
دوشنبه 16 شهریور 1394, 10:32 صبح
سلام.
مدتی میشه که برای نصب برنامه هام روی یک Installer کار کردم و کم کم بهش امکانات اضافه کردم.
هنوز یک مقدار برای جزئیاتش نیاز به کار داره.
امکانات فعلی:
تعریف package های مختلف برای سیستم عامل های مختلف با uri های قابل تنظیم و آدرس نصب قابل تنظیم.
http, https, ftp, ...
x509 و قابلیت تغییر certificate با هر update
SSDP برای نصب در شبکه داخلی
تست صحت manifest با gpg
بررسی صحت فایلها با sha256
command line parser خیلی عالی
قابلیت تنظیم فایل اجرایی برای هر package
حجم خیلی پایین و بدون وابستگی
اجرا در windows/linux x86/x86_64
قابلیت تعریف چند source
قابلیت blacklist فایلها
قبالیت نگهداری فایلهای تنظیمات و عدم بروزرسانی آنها در صورت تغییر توسط کاربر

امکاناتی که نداریم و یا تکمیل نیستند:
GUI, automation API, scripting, symlink, platform exclusion

نمونه invocation:

#!/bin/bash
rm manifest.xml
mono Behrooz.WebInstaller.exe configure \
--package name=linux-common install-path= platform=Linux remote-path=platforms/linux-common/ \
--package name=linux-i386 install-path= platform=Linux remote-path=platforms/linux-i386/ bits=32 \
--package name=linux-86_64 install-path= platform=Linux remote-path=platforms/linux-x86_64/ bits=64 \
--package name=win-common install-path= platform=Windows remote-path=platforms/win-common/ \
--package name=win32 install-path= platform=Windows remote-path=platforms/win32/ bits=32 \
--package name=win64 install-path= platform=Windows remote-path=platforms/win64/ bits=64 \
--package name=bin install-path= platform=Common remote-path= executable=Application.exe \
--auto-discovery --uri "http://example.com/update/" \
--certificate "../example.com.crt" --version 1 --output manifest.xml
gpg --output manifest.xml.sig --detach-sig manifest.xml
rsync -v -e 'ssh -2C' -v --delete --checksum -Pa --checksum --inplace ./ update-agent@example.com:/var/www-example.com/update

source code با لایسنس GPL منتشر میشه.
یعنی اگر کدش رو به برنامتون اضافه نکنید و بهش رفرنس نزنید و فقط خودش رو با تنظیماتش کنار برنامه بزارید هیچ مشکلی نداره.برای هیچ گونه استفاده ای لازم نیست بهش رفرنس بزنید پس نباید مشکلی باشه.

خواهش میکنم تغییراتتان(patch) رو اینجا بفرستید تا به کد اصلی اضافه بشن که همه بتونن استفاده کنن.

CLA: هر گونه تغییرات(patch) ارسال شده بدون حق مالکیت است و فرستنده تغییرات(patch) در برابر آنها و هر گونه استفاده از آنها هیچ ادعایی ندارد.



این کد رو برای این منتشر کردم که نمونه هایی مثل http://barnamenevis.org/showthread.php?412932 وجود دارند که برنامه نویسان زیادی بدون اینکه اطلاع داشته باشند قسمتی از برنامشون چطوری کار میکنه و عملا هیچ مکانیزم امنیتی نداره مجبور هستند از این گونه برنامه ها استفاده کنند.
امیدوارم افرادی که از این کد استفاده میکنند و درش تغییر ایجاد میکنند تغییرات رو برای بقیه بفرستند.

FastCode
دوشنبه 16 شهریور 1394, 10:34 صبح
reserved post

rahnema1
دوشنبه 16 شهریور 1394, 22:09 عصر
خدا خیرتون بده. کار زیادی برده. فقط اگه بتونید نحوه استفاده را بیشتر توضیح بدید ( مخصوصا در ویندوز) ممنون می شیم. این لایسنس GPL هم معنیش اینه که اگه از نرم افزار شما در برنامه های خودمون استفاده کردیم. برنامه خودمون هم GPL می شه و حتما باید سورسش را در اختیار بقیه بذاریم درسته؟

FastCode
سه شنبه 17 شهریور 1394, 01:46 صبح
خدا خیرتون بده. کار زیادی برده. فقط اگه بتونید نحوه استفاده را بیشتر توضیح بدید ( مخصوصا در ویندوز) ممنون می شیم. این لایسنس GPL هم معنیش اینه که اگه از نرم افزار شما در برنامه های خودمون استفاده کردیم. برنامه خودمون هم GPL می شه و حتما باید سورسش را در اختیار بقیه بذاریم درسته؟

سلام.
بله. یکی دو تا نمونه دیگه هم برای استفادش میزارم.
خوشبختانه بخاطر ساختار این برنامه لازم نیست که بهش رفرنس بزنید یا از کدش توی برنامتون استفاده کنید. بنابراین مجبور نیستید برنامتون رو gpl منتشر کنید.
این کد این برنامه رو روی NIC های مختلف در حالت SSDPd اجرا میکنه:

using System;
using System.Net.NetworkInformation;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace Behrooz.Dev.InstallerDaemon
{
static class MainClass
{
/// <summary>
/// Returns the IPv4 or IPv6 address in standard notation. Any transitional suffix (i.e. an IPv4-like address
/// displayed in place of the final two segments of an IPv6 address) returned by .NET is converted to standard colon notation.
/// See http://stackoverflow.com/questions/4863352/what-dictates-the-formatting-of-ipv6-addresses-by-system-net-ipaddress-tostring.
/// </summary>
public static string ToStringNonTransitional (this System.Net.IPAddress oIPAddress)
{
var sIP = oIPAddress.ToString ();

if (oIPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
return sIP; // Return IPv4 addresses untouched.

if (oIPAddress.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6)
throw new Exception (string.Format ("Can't handle '{0}' in '{1}' format. (Only IPv4 or IPv6 supported.)", sIP, oIPAddress.AddressFamily.ToString ()));

if (!sIP.Contains ("."))
return sIP;

try {
var iTransitionalStart = sIP.LastIndexOf (":", StringComparison.Ordinal) + 1;
var sTransitionalPart = sIP.Substring (iTransitionalStart);
sIP = sIP.Substring (0, iTransitionalStart);
var asOctects = sTransitionalPart.Split ('.');
sIP += string.Format ("{0:x2}{1:x2}", Convert.ToInt16 (asOctects [0]), Convert.ToInt16 (asOctects [1])).TrimStart ('0');
sIP += ":" + string.Format ("{0:x2}{1:x2}", Convert.ToInt16 (asOctects [2]), Convert.ToInt16 (asOctects [3])).TrimStart ('0');

return sIP;
} catch (Exception ex) {
throw new Exception ("Failed to convert IPv6 address to standard notation: " + sIP, ex);
}
}

static string IpToString (UnicastIPAddressInformation unicastIPAddressInformation)
{
string ipString;
if (unicastIPAddressInformation.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) {
ipString = unicastIPAddressInformation.Address.ToStringNonTra nsitional ();
int scopeOffset = ipString.IndexOfAny (new[] {
'%'
});
if (scopeOffset != -1) {
ipString = ipString.Substring (0, scopeOffset);
}
ipString = "[" + ipString + "]";
} else {
ipString = unicastIPAddressInformation.Address.ToString ();
}
return ipString;
}

static bool checkIPVersion (UnicastIPAddressInformation iP, bool ipv4, bool ipv6)
{
if (iP.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
return ipv4;
if (iP.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
return ipv6;
return false;
}

static void chmod ()
{
foreach (var file in System.IO.Directory.EnumerateFiles ("bin", "*", SearchOption.AllDirectories))
Mono.Unix.Native.Syscall.chmod (file, Mono.Unix.Native.FilePermissions.DEFFILEMODE);
}

static Guid? serverID = null;
static bool no_ipv4 = true;
static bool no_ipv6 = true;
static readonly List<string> interfaces = new List<string> ();
static bool exclude = false;
static string url = "espand";
static string extra = "https://example.com/update/|http://example.com/update/";

static void set_extra (string value)
{
extra = value;
}

static void set_overlay (string value)
{
extra = value + "|" + extra;
}

static void add_interface (string value)
{
interfaces.Add (value);
}

static void set_serverid (string value)
{
serverID = Guid.Parse (value);
}

static void set_url (string value)
{
url = value;
}

static void help ()
{
Console.WriteLine ("Behroz.Dev.InstallerDaemon - Behrooz Installer Daemon Manager CLI version 1");
Console.WriteLine (" requires Behrooz.WebInstaller v4+");
Console.WriteLine (" -4, --no-ipv4 Disable ipv4 - default: {0}", no_ipv4);
Console.WriteLine (" -6, --no-ipv6 Disable ipv6 - default: {0}", no_ipv6);
Console.WriteLine (" -e, --exclude Invert interface matching");
Console.WriteLine (" -i, --interface Interfaces to broadcast to");
Console.WriteLine (" -u, --url Base URL for manifest.xml - default: {0}", url);
Console.WriteLine (" -e, --extra Extra urls for manifest");
Console.WriteLine (" -o, --overlay Extra urls for manifest (prepended)");
Console.WriteLine (" -s, --serverid Server GUID for SSDP broadcasts");
Console.WriteLine (" -h, --help This help - close without action");
}

public static void Main (string[] args)
{

for (int argn = 0; argn < args.Length; argn++) {
if (args [argn].StartsWith ("--", StringComparison.Ordinal)) {
switch (args [argn]) {
case "--extra":
set_extra (args [argn + 1]);
break;
case "--overlay":
set_overlay (args [argn + 1]);
break;
case "--exclude":
exclude = true;
break;
case "--url":
set_url (args [argn + 1]);
break;
case "--interface":
add_interface (args [argn + 1]);
break;
case "--no-ipv4":
no_ipv4 = false;
break;
case "--no-ipv6":
no_ipv6 = false;
break;
case "--serverid":
set_serverid (args [argn + 1]);
break;
case "--help":
help ();
return;
}
} else {
args [argn] = args [argn].Substring (1);
bool usedPara = false;
foreach (char c in args[argn]) {
if (usedPara)
throw new ArgumentException ("Bad argument");
switch (c) {
case 'e':
set_extra (args [++argn]);
usedPara = true;
break;
case 'o':
set_overlay (args [++argn]);
usedPara = true;
break;
case 'x':
exclude = true;
break;
case 'u':
set_url (args [++argn]);
usedPara = true;
break;
case 'i':
add_interface (args [++argn]);
usedPara = true;
break;
case '4':
no_ipv4 = false;
break;
case '6':
no_ipv6 = false;
break;
case 's':
set_serverid (args [++argn]);
usedPara = true;
break;
case 'h':
help ();
return;
}
}
}
}
if (serverID == null)
serverID = Guid.NewGuid ();
foreach (var file in System.IO.Directory.EnumerateFiles ("bin", "bd2 - *.log"))
System.IO.File.Delete (file);
foreach (var file in System.IO.Directory.EnumerateFiles ("bin", "*.xml~", SearchOption.AllDirectories))
System.IO.File.Delete (file);
foreach (var file in System.IO.Directory.EnumerateFiles ("bin", "manifest.xml*"))
System.IO.File.Delete (file);
if (System.Environment.OSVersion.Platform != PlatformID.Win32NT) {
chmod ();
}
try {
string flist = string.Join (" ", System.IO.Directory.GetFiles ("Behrooz.ABI", "*.cs"));
if (!System.Diagnostics.Process.Start ("tar", "cf bin/ABI-GPL.tar " + flist).WaitForExit (1500)) {
Console.Error.WriteLine ("tar timed out");
}
} catch {
Console.Error.WriteLine ("tar failed to execute");
}
foreach (var file in System.IO.Directory.EnumerateFiles ("BD2", "*.dll"))
System.IO.File.Copy (file, "bin" + file.Substring (file.LastIndexOf ('/')), true);
var nics = System.Net.NetworkInformation.NetworkInterface.Get AllNetworkInterfaces ();
List<UnicastIPAddressInformationCollection> uipaics = new List<UnicastIPAddressInformationCollection> ();
foreach (var nic in nics) {
Console.Write ("nic: {0} ", nic.Name);
if (exclude == (interfaces.Count == 0 || interfaces.Contains (nic.Name))) {
Console.WriteLine ("skipped - list match");
continue; //we don't want it
}
if (nic.OperationalStatus != OperationalStatus.Up) {
Console.WriteLine ("skipped - not up");
continue; // this adapter is off or not connected
}
if (!nic.SupportsMulticast) {
Console.WriteLine ("skipped - no multicast");
continue; // we need multicast
}
var p = nic.GetIPProperties ().UnicastAddresses;
if (null == p) {
Console.WriteLine ("skipped - no unicast");
continue; // no unicast ip for incoming tcp
}
Console.WriteLine ("selected");
uipaics.Add (p);
}
string URLs = "";
bool anyIP = false;
foreach (var p in uipaics) {
foreach (var IP in p) {
if (!checkIPVersion (IP, no_ipv4, no_ipv6))
continue;
anyIP = true;
URLs += "--uri http://" + IpToString (IP) + "/" + url + "/ ";
}
}
if (!anyIP) {
Console.WriteLine ("no ip");
return;
}
if (System.IO.File.Exists ("bin/manifest.xml")) {
System.IO.File.Delete ("bin/manifest.xml");
}
Console.WriteLine ("executing");
var genManifest = new Process ();
genManifest.StartInfo.FileName = "mono";
genManifest.StartInfo.WorkingDirectory = Path.Combine (Process.GetCurrentProcess ().StartInfo.WorkingDirectory, "bin");
genManifest.StartInfo.Arguments = "Behrooz.WebInstaller.exe configure " +
"--package name=linux-common install-path= platform=Linux remote-path=platforms/linux-common/ " +
"--package name=linux-i386 install-path= platform=Linux remote-path=platforms/linux-i386/ bits=32 " +
"--package name=linux-x86_64 install-path= platform=Linux remote-path=platforms/linux-x86_64/ bits=64 " +
"--package name=win-common install-path= platform=Windows remote-path=platforms/win-common/ " +
"--package name=win32 install-path= platform=Windows remote-path=platforms/win32/ bits=32 " +
"--package name=win64 install-path= platform=Windows remote-path=platforms/win64/ bits=64 " +
"--package name=bin install-path=\"\" platform=Common remote-path= executable=Application.exe " +
"--auto-discovery " + URLs +
"--certificate \"../example.com.crt\" --version 1 --output manifest.xml";
genManifest.Start ();
genManifest.WaitForExit ();
Process.Start ("gpg", "--output bin/manifest.xml.sig --detach-sig bin/manifest.xml").WaitForExit ();
Process gpg = Process.Start ("gpg", "--verify bin/manifest.xml.sig bin/manifest.xml");
gpg.WaitForExit ();
int ec = gpg.ExitCode;
if (ec != 0) {
Console.Error.WriteLine ("***GPG SIGNING FAILED. ABORTING.***");
return;
}
List<Process> processes = new List<System.Diagnostics.Process> ();
foreach (var p in uipaics) {
foreach (var IP in p) {
if (!checkIPVersion (IP, no_ipv4, no_ipv6))
continue;
string ipString = IpToString (IP);
Process process = Process.Start ("bin/Behrooz.WebInstaller.exe", "daemonize --port 18100 --uri \"http://" + ipString + "/" + url + "/\" --server-id " + serverID.GetValueOrDefault ());
processes.Add (process);
Console.WriteLine ("[{0}] \"http://" + ipString + "/" + url + "/", process.Id);
}
}
try {
while (processes.Count > 0) {
foreach (var p in new List<Process>(processes)) {
p.WaitForExit (100);
if (p.HasExited) {
Console.WriteLine ("[{0}] died", p.Id);
processes.Remove (p);
}
}
}
} finally {
foreach (var p in processes) {
if (!p.HasExited) {
p.Kill ();
}
}
}
}
}
}


توجه داشته باشید که این یک نمونست و باید بسته به نیازتون تغییرش بدید و فقط روی کامپیوتر خودتون اجرا میکنید که SSDP server رو اجرا کنه.
فرض شده که پوشه bin در /update ه webserver شما mount شده.
برای نصب برنامه هم فقط کافیه بدون آرگومان اجراش کنید. البته میتونید اینطوری هم اجرا کنید:
Behrooz.WebInstaller install
که اینطوری ازتون چند تا آرگومان ساده مثل no-execute --force --manifest-- و ... میگیره.

rahnema1
سه شنبه 17 شهریور 1394, 08:27 صبح
واقعا زحمت کشیدید. امکانش هست که مثلا این پروژه با پروژه ای مثل squirrel ادغام بشه؟ یا مثلا با clickOnce مربوط به دات نت سازگاری پیدا کنه؟

FastCode
سه شنبه 17 شهریور 1394, 10:21 صبح
واقعا زحمت کشیدید. امکانش هست که مثلا این پروژه با پروژه ای مثل squirrel ادغام بشه؟ یا مثلا با clickOnce مربوط به دات نت سازگاری پیدا کنه؟
سلام.
من با squirrel کار نکردم و نمیدونم اصلا چی هست. کدهای پست ۳ هیچ لایسنسی ندارند و هر طوری دوست دارید میتونید استفاده کنید.

negar.rafie
جمعه 20 شهریور 1394, 09:37 صبح
میشه اقای fastcode توضیح بدید از این اینستالر کجا میشه استفاده کرد؟

FastCode
شنبه 21 شهریور 1394, 05:10 صبح
میشه اقای fastcode توضیح بدید از این اینستالر کجا میشه استفاده کرد؟

سلام.
این اینستالر برای پروژه هایی هست که به به روز رسانی های کوچک و مداوم دارند.یعنی مثلا تعداد چند صد فایل که در هر به روز رسانی فقط چندتاشون تغییر میکنند.
هر فایل ممکنه برای یک یا چند سیستم عامل هدف باشه.

برای محیط تست موقع نوشتن برنامه هم خیلی کمک میکنه چون لازم نیست اطلاعات رو روی هاستی خارج از شبکه محلیتون آپلود کنید.