-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Windows Installer
fabionogueira edited this page Jun 16, 2015
·
8 revisions
Dependencies:
- 7-Zip
- sed.exe
- Bat_To_Exe_Converter.exe
- Inno Setup 5
- Resourcer.exe
Directory tree created by the batch:
dist\release\{version}\start.exe dist\release\{version}\bin\application.exe
dist\release\{version}\bin\nw.pak
dist\release\{version}\bin\libEGL.dll
dist\release\{version}\bin\...
dist\installer\installer_{version}.exe
dist\update\package.json
dist\update\app_v{version}.zip
Batch
@echo off
@cls
set name=unity
set version=1.0.1
set updater_url=http://url/to/update/package.json
set path_dist=C:\path\to\dist
set path_www=C:\path\to\www
set path_icon=C:\path\to\icon.ico
set path_7zip="C:\path\to\7-Zip\7z"
set path_sed="C:\path\to\sed.exe"
set path_bat2exe=C:\path\to\Bat_To_Exe_Converter.exe"
set path_inno_setup_exe="C:\path\to\InnoSetup\ISCC.exe"
set path_icon_resource="C:\path\to\Resource"
set path_inno_setup_scp=C:\path\to\script.iss
set path_bin=C:\path\to\nw_bin
set path_bat=C:\path\to\start.bat
set path_tools=C:\path\to\tools
set mypath=%CD%
@cd %mypath%
rem cria a pasta release\%version%\bin
%path_release_version% quando existia ficava bloqueada
echo.
echo starting compilation version "%version%"...
set path_release_version=%path_dist%\release\%version%
set path_update_version=%path_dist%\update\%version%
if not exist %path_dist%\release md %path_dist%\release
if not exist %path_dist%\update md %path_dist%\update
if not exist %path_dist%\installer md %path_dist%\installer
if exist %path_release_version% rd /s /q %path_release_version%
if exist %path_update_version% rd /s /q %path_update_version%
ping -n 1 127.0.0.1 > nul
md %path_release_version%
md %path_release_version%\bin
md %path_release_version%\tools
md %path_release_version%\sed
md %path_release_version%\www
md %path_update_version%
del /q %path_release_version%\sed\*
rem cria o executável inicial, start.bat para .exe
echo creating started executable...
%path_bat2exe% -overwrite -invisible -bat %path_bat% -save %path_release_version%\%name%.exe -icon %path_icon% > nul
rem copia os binários
echo copy binaries...
copy %path_bin%\* %path_release_version%\bin > nul
copy %path_tools%\* %path_release_version%\tools > nul
if exist %path_release_version%\bin\package.json del %path_release_version%\bin\package.json
rem cria o zip www. substitui {{version}} em package.json
echo creating www.zip...
xcopy %path_www%\* %path_release_version%\www /s > nul
@cd %path_release_version%\sed
%path_sed% -i "s/{{version}}/%version%/g" %path_release_version%\www\package.json
@cd %path_release_version%\www
%path_7zip% a -tzip %path_release_version%\www.zip * > nul
@cd %mypath%
rd %path_release_version%\www /s /q
rd %path_release_version%\sed /s /q
rem cria um executável único com os arquivos www
echo creating application.exe...
cd %path_icon_resource%
Resourcer -op:upd -src:%path_release_version%\bin\nw.exe -type:14 -name:IDR_MAINFRAME -file:%path_icon% > nul
copy /b %path_release_version%\bin\nw.exe+%path_release_version%\www.zip %path_release_version%\bin\application.exe > nul
rem exclui alguns arquivos (limpeza)
del %path_release_version%\www.zip
del %path_release_version%\bin\nw.exe
if exist %path_release_version%\bin\start.bat del %path_release_version%\bin\start.bat
if exist %path_release_version%\bin\run.bat del %path_release_version%\bin\run.bat
if exist %path_release_version%\bin\credits.html del %path_release_version%\bin\credits.html
if exist %path_release_version%\bin\nwjc.exe del %path_release_version%\bin\nwjc.exe
rem cria o zip para atualização automática
echo creating bin update zip...
cd %path_release_version%\bin
%path_7zip% a -tzip %path_update_version%\%name%_%version%.zip * > nul
cd %mypath%
rem cria package.json
echo {"version":"%version%","location":"%updater_url%","file":"%name%_%version%.zip"} > %path_update_version%\package.json
rem cria o instalador
echo creating installer...
md %path_release_version%\sed\
copy %path_inno_setup_scp% %path_release_version%\sed\script.iss > nul
@cd %path_release_version%\sed
%path_sed% -i "s/{{version}}/%version%/g" %path_release_version%\sed\script.iss
@cd %mypath%
%path_inno_setup_exe% /cc %path_release_version%\sed\script.iss
rd %path_release_version%\sed /s /q
:end
echo finish.
start.bat
@echo off
cd %CD%
rem pausa por 3 segundos
if "%1" == "--restart" (
ping -n 3 127.0.0.1 > nul
)
if exist bin_new (
if exist bin (
ren bin bin_old
)
ren bin_new bin
)
if exist bin_old (
rd bin_old /s /q
)
start /I bin\application.exe
script.iss
#define MyAppName "MyAppName"
#define MyAppVersion "{{version}}"
#define MyAppPublisher "My Company, Inc."
#define MyAppURL "http://my.url.to.app/"
#define MyAppExeName "MyApp.exe"
[Setup]
AppId={{[email protected]}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName}v{#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName=C:\{#MyAppName}
DefaultGroupName={#MyAppName}
OutputDir=C:\path\to\installer
OutputBaseFilename=Unity_v{#MyAppVersion}
Compression=lzma
SolidCompression=yes
[Languages]
Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "C:\path\to\dist\release\{#MyAppVersion}\bin\*"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "C:\path\to\dist\release\{#MyAppVersion}\tools\*"; DestDir: "{app}\tools"; Flags: ignoreversion
Source: "C:\path\to\dist\release\{#MyAppVersion}\Myapp.exe"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
Updater.js
/**
* @class Updater
* @version 0.0.1
* @author Fábio Nogueira
*/
var Updater = (function(){
var
fs = require('fs'),
http = require('http'),
path = require('path'),
gui = require('nw.gui'),
PATH_ALL_USER = process.env.ALLUSERSPROFILE + '/.ld/', //HOMEDRIVE
DATA = {
status: 0,
version: '',
newVersion: '',
releaseNote: '',
downloadPercent: 0,
available: false,
err: ''
},
EXE, self,
STATUS_NONE=0, STATUS_CHECKING=1, STATUS_DOWNLOADING=2,
PATH_APP, FS, APP_NAME, Updater;
FS = {
copyFiles: function(sources, folder, next){
var i=-1;
function runCopy(){
i++;
if (i>=sources.length){
return next();
}
console.log(i, sources[i] + ' > ' + folder + '/' + path.basename(sources[i]))
FS.copy(sources[i], folder + '/' + path.basename(sources[i]), runCopy);
}
runCopy();
},
copy: function(source, target, next) {
// copia um arquivo para outro local sobrescrevendo o atual, se existir.
var rd, wr, cbCalled = false;
rd = fs.createReadStream(source);
rd.on("error", done);
wr = fs.createWriteStream(target);
wr.on("error", done);
wr.on("close", function(ex) {
done();
});
rd.pipe(wr);
function done(err) {
if (!cbCalled) {
next(err);
cbCalled = true;
}
}
},
createPath: function(dir, next){
fs.lstat(dir, function (err, stats){
var
exists;
if (err) {
exists = !(err.code==='ENOENT');
}else{
exists = stats.isDirectory();
}
if (!exists){
fs.mkdir(dir, '0777', function(err){
next(err ? false : true);
});
}else{
next(true);
}
});
},
removePath: function(path, next){
var files = [];
if( fs.existsSync(path) ) {
files = fs.readdirSync(path);
files.forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) { // recurse
FS.removePath(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
if (next) next(false);
}
},
rename: function(oldName, newName, next, count){
count = count===undefined ? 5 : count;
fs.rename(oldName, newName, function(err){
if (err){
count--;
if (count>0){
setTimeout(function(){FS.rename(oldName, newName, next, count);}, 400);
}else{
console.error(err);
next(false);
}
}else{
next(true);
}
});
},
cmd: function(command, args, next){
var
spawn = require('child_process').spawn,
cmd;
cmd = spawn(command, args, {detached: true});//, cwd:path.normalize(process.execPath+'/')});
if (next){
cmd.stdout.on('data', function(data) {
});
cmd.on('exit', function(code){
if (next) next (code===0);
});
cmd.on('error', function(err){
if (next) next (false, err.message);
});
}
},
unzip: function(file, dest, next){
var root = path.dirname(path.normalize(process.execPath+'/../')) + '/',
command = root+'tools/unzip.exe',
args = ['-o', file, '-d', dest];
command = command.replace(/\\/g, '/');
file = file.replace(/\\/g, '/');
dest = dest.replace(/\\/g, '/');
this.cmd(command, args, next);
}
};
//private functions:
function readFile(file, next) {
fs.readFile(file, 'utf8', function(err, data) {
if (err){
dispatch(INSTALLER_CB, 'log', ' error, code: '+err.code+'');
}else{
dispatch(INSTALLER_CB, 'log', ' OK');
}
next(err ? null : data);
});
}
function writeFile(file, content, next){
dispatch(INSTALLER_CB, 'log', '\nwrite file "' + file + '"...');
fs.writeFile(PATH_APP + file, content, function (err){
if (err){
dispatch(INSTALLER_CB, 'log', ' error, code: '+err.code+'');
}else{
dispatch(INSTALLER_CB, 'log', ' OK');
}
next ? next(err ? false : true) : null;
});
}
function normalizeUrl(url){
url = url.replace(/\\/g,'/');
url = url.replace(/\/\//g,'/');
url = url.replace(/http:\//g,'http://');
return url;
}
Object.observe(DATA, function(){
self.onChange(DATA);
});
self = {
onChange: function(){},
data: function(){
return DATA;
},
createPaths: function(next){
//cria os diret�rio: ALLUSERS/.livred/apps/[appname]/
var
f = ['apps', APP_NAME];
dispatch(INSTALLER_CB, 'log', '\ncreate paths...');
function create(path, index){
FS.createPath(path, function(success){
if (success){
path += (f[index] + '/');
if (index < f.length){
index++;
create(path, index);
}else{
dispatch(INSTALLER_CB, 'log', ' OK');
next(true);
}
}else{
dispatch(INSTALLER_CB, 'log', ' error');
next(false);
}
});
}
create(PATH_ALL_USER, 0);
},
checkForUpdate: function(urls, next) {
var index = -1;
function check(url){
http.request(url, function(res, err) {
if (err || res.statusCode !== 200) {
DATA.err = err ? err.code : res.statusCode;
nextUrl();
} else {
res.on('data', function(chunk) {
var result;
try{
result = JSON.parse(chunk.toString());
if (result.version > DATA.newVersion){
DATA.newVersion = result.version;
result.location = result.location || url;
nextUrl(result);
}else{
nextUrl();
}
}catch(_e){
nextUrl();
}
});
}
})
.on('error', function(err) {
nextUrl();
})
.end();
}
function nextUrl(res){
index++;
if ( res || !urls[index] ) {
DATA.status = STATUS_NONE;
return next(res || null);
}
check(urls[index]);
}
nextUrl();
},
download: function(url, dest, next){
var request;
DATA.downloadPercent = 0;
request = http.request(url, function (res, err) {
var file, percent, oldPercent,
count = 0,
len = parseInt(res.headers['content-length'], 10);
if (err || res.statusCode!==200) {
DATA.err = 'download_error: (' + url + ') ' + (err ? err.code : res.statusCode);
next();
} else {
file = fs.createWriteStream(dest+'.part');
file.on('finish', function() {
DATA.downloadPercent = 100 ;
if (count===len){
FS.rename(dest+'.part', dest, function(success){
if (success){
next(true);
}else{
DATA.err = 'error';
next();
}
});
}else{
DATA.err = "download aborded.";
next();
}
});
res.pipe(file);
res.on('data', function (chunk) {
if (chunk.length){
count += chunk.length;
percent = parseInt((count*100) / len);
if (percent!==oldPercent){
DATA.downloadPercent = percent;
}
oldPercent = percent;
}
});
request.setTimeout(1200, function(){
request.abort(); //isso faz com que chame o file.on('finish', ...
});
}
});
request.on('error', function(err){
DATA.err = 'download_error: (' + url + ') ' + err.code;
next();
});
request.end();
},
restart: function(){
var child, child_process, win,
executable = path.dirname(path.normalize(process.execPath+'/../')) + '/' + EXE;
child_process = require("child_process");
win = gui.Window.get();
child = child_process.spawn(executable, ['--restart'], {detached: true, cwd:path.dirname(executable)});
child.unref();
win.hide();
gui.App.quit();
},
init: function() {
var manifest= gui.App.manifest,
root = path.dirname(path.normalize(process.execPath+'/../')) + '/',
urls, zipName;
if (!manifest.updater) return;
if (!manifest.updater.locations) return;
manifest.updater.interval = manifest.updater.interval || 20000;
EXE = path.basename(process.execPath);
zipName = root + EXE + '_update.zip';
urls = manifest.updater.locations.split(';');
DATA.version = DATA.newVersion = (manifest.version || "0.0.0");
function nextCheck(){
if (DATA.err !== ''){
DATA.newVersion = DATA.version;
}
DATA.status = STATUS_CHECKING;
DATA.err = '';
DATA.downloadPercent = 0;
self.checkForUpdate(urls, function(res){
var urlFileDownload;
if (res){
DATA.status = STATUS_DOWNLOADING;
urlFileDownload = normalizeUrl(res.location+'/') + (res.file || EXE.replace('.exe', '_v'+res.version+'.zip'));
self.download(urlFileDownload, zipName, function(success){
DATA.status = STATUS_NONE;
if (!success) {
DATA.newVersion = DATA.version;
DATA.downloadPercent = 0;
setTimeout(nextCheck, manifest.updater.interval);
}else{
FS.unzip(zipName, root+'bin_new', function(success, err) {
if (success) {
DATA.available = true; //significa que tem uma nova vers�o em uma pasta bin_new
//exclui o arquivo zipado
fs.unlink(zipName, function(err) {
setTimeout(nextCheck, manifest.updater.interval);
});
}else{
DATA.err = err;
setTimeout(nextCheck, manifest.updater.interval);
}
});
}
});
}else{
DATA.status = STATUS_NONE;
setTimeout(nextCheck, manifest.updater.interval);
}
});
}
nextCheck();
return self;
}
};
return self;
}());
index.html
<title>Test</title>
<script src="Updater.js" type="text/javascript"></script>
</head>
<body>
<div id="log" style="display:none;position:absolute;bottom:0;left:0;right:0;height:200px;background:#fff"></div>
<script>
var title = 'Unity';
function jsonToString(data){
var i, html='<pre>{\n';
for (i in data){
html += (' "'+i+'": "'+data[i]+'"\n');
}
html+='}</pre>';
return html;
}
Updater.onChange = function(data){
document.getElementById('log').innerHTML = jsonToString(data)
};
Updater.init();
</script>
</body>