@oclif/dev-cli
Version:
helpers for oclif CLIs
236 lines (211 loc) • 8.16 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@oclif/command");
const qq = require("qqjs");
const Tarballs = require("../../tarballs");
const scripts = {
/* eslint-disable no-useless-escape */
cmd: (config) => ` off
setlocal enableextensions
set ${config.scopedEnvVarKey('BINPATH')}=%~dp0\\${config.bin}.cmd
if exist "%LOCALAPPDATA%\\${config.dirname}\\client\\bin\\${config.bin}.cmd" (
"%LOCALAPPDATA%\\${config.dirname}\\client\\bin\\${config.bin}.cmd" %*
) else (
"%~dp0\\..\\client\\bin\\node.exe" "%~dp0\\..\\client\\bin\\run" %*
)
`,
sh: (config) => `#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")
"$basedir/../client/bin/${config.bin}.cmd" "$@"
ret=$?
exit $ret
`,
nsis: (config, arch) => `!include MUI2.nsh
!define Version '${config.version.split('-')[0]}'
Name "${config.name}"
CRCCheck On
InstallDirRegKey HKCU "Software\\${config.name}" ""
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
OutFile "installer.exe"
VIProductVersion "\${VERSION}.0"
VIAddVersionKey /LANG=\${LANG_ENGLISH} "ProductName" "${config.name}"
VIAddVersionKey /LANG=\${LANG_ENGLISH} "Comments" "${config.pjson.homepage}"
VIAddVersionKey /LANG=\${LANG_ENGLISH} "CompanyName" "${config.scopedEnvVar('AUTHOR') || config.pjson.author}"
VIAddVersionKey /LANG=\${LANG_ENGLISH} "LegalCopyright" "${new Date().getFullYear()}"
VIAddVersionKey /LANG=\${LANG_ENGLISH} "FileDescription" "${config.pjson.description}"
VIAddVersionKey /LANG=\${LANG_ENGLISH} "FileVersion" "\${VERSION}.0"
VIAddVersionKey /LANG=\${LANG_ENGLISH} "ProductVersion" "\${VERSION}.0"
InstallDir "\$PROGRAMFILES${arch === 'x64' ? '64' : ''}\\${config.dirname}"
Section "${config.name} CLI \${VERSION}"
SetOutPath $INSTDIR
File /r bin
File /r client
ExpandEnvStrings $0 "%COMSPEC%"
WriteRegStr HKCU "Software\\${config.dirname}" "" $INSTDIR
WriteUninstaller "$INSTDIR\\Uninstall.exe"
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${config.dirname}" \\
"DisplayName" "${config.name}"
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${config.dirname}" \\
"UninstallString" "$\\"$INSTDIR\\uninstall.exe$\\""
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${config.dirname}" \\
"Publisher" "${config.scopedEnvVar('AUTHOR') || config.pjson.author}"
SectionEnd
Section "Set PATH to ${config.name}"
Push "$INSTDIR\\bin"
Call AddToPath
SectionEnd
Section "Add %LOCALAPPDATA%\\${config.dirname} to Windows Defender exclusions (highly recommended for performance!)"
ExecShell "" '"$0"' "/C powershell -ExecutionPolicy Bypass -Command $\\"& {Add-MpPreference -ExclusionPath $\\"$LOCALAPPDATA\\${config.dirname}$\\"}$\\" -FFFeatureOff" SW_HIDE
SectionEnd
Section "Uninstall"
Delete "$INSTDIR\\Uninstall.exe"
RMDir /r "$INSTDIR"
RMDir /r "$LOCALAPPDATA\\${config.dirname}"
DeleteRegKey /ifempty HKCU "Software\\${config.dirname}"
DeleteRegKey HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${config.dirname}"
SectionEnd
!define Environ 'HKCU "Environment"'
Function AddToPath
Exch $0
Push $1
Push $2
Push $3
Push $4
; NSIS ReadRegStr returns empty string on string overflow
; Native calls are used here to check actual length of PATH
; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3)
System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4"
IntCmp $4 0 0 done done
; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
; RegCloseKey($3)
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i \${NSIS_MAX_STRLEN} r2) i.r4"
System::Call "advapi32::RegCloseKey(i $3)"
IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
DetailPrint "AddToPath: original length $2 > \${NSIS_MAX_STRLEN}"
MessageBox MB_OK "PATH not updated, original length $2 > \${NSIS_MAX_STRLEN}"
Goto done
IntCmp $4 0 +5 ; $4 != NO_ERROR
IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
DetailPrint "AddToPath: unexpected error code $4"
Goto done
StrCpy $1 ""
; Check if already in PATH
Push "$1;"
Push "$0;"
Call StrStr
Pop $2
StrCmp $2 "" 0 done
Push "$1;"
Push "$0\\;"
Call StrStr
Pop $2
StrCmp $2 "" 0 done
; Prevent NSIS string overflow
StrLen $2 $0
StrLen $3 $1
IntOp $2 $2 + $3
IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
IntCmp $2 \${NSIS_MAX_STRLEN} +4 +4 0
DetailPrint "AddToPath: new length $2 > \${NSIS_MAX_STRLEN}"
MessageBox MB_OK "PATH not updated, new length $2 > \${NSIS_MAX_STRLEN}."
Goto done
; Append dir to PATH
DetailPrint "Add to PATH: $0"
StrCpy $2 $1 1 -1
StrCmp $2 ";" 0 +2
StrCpy $1 $1 -1 ; remove trailing ';'
StrCmp $1 "" +2 ; no leading ';'
StrCpy $0 "$1;$0"
WriteRegExpandStr \${Environ} "PATH" $0
SendMessage \${HWND_BROADCAST} \${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
done:
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd
; StrStr - find substring in a string
;
; Usage:
; Push "this is some string"
; Push "some"
; Call StrStr
; Pop $0 ; "some string"
Function StrStr
Exch $R1 ; $R1=substring, stack=[old$R1,string,...]
Exch ; stack=[string,old$R1,...]
Exch $R2 ; $R2=string, stack=[old$R2,old$R1,...]
Push $R3
Push $R4
Push $R5
StrLen $R3 $R1
StrCpy $R4 0
; $R1=substring, $R2=string, $R3=strlen(substring)
; $R4=count, $R5=tmp
loop:
StrCpy $R5 $R2 $R3 $R4
StrCmp $R5 $R1 done
StrCmp $R5 "" done
IntOp $R4 $R4 + 1
Goto loop
done:
StrCpy $R1 $R2 "" $R4
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Exch $R1 ; $R1=old$R1, stack=[result,...]
FunctionEnd
`,
};
class PackWin extends command_1.Command {
async run() {
await this.checkForNSIS();
const { flags } = this.parse(PackWin);
const buildConfig = await Tarballs.buildConfig(flags.root);
const { config } = buildConfig;
await Tarballs.build(buildConfig, { platform: 'win32', pack: false });
const arches = buildConfig.targets.filter(t => t.platform === 'win32').map(t => t.arch);
for (const arch of arches) {
const installerBase = qq.join(buildConfig.tmp, `windows-${arch}-installer`);
// eslint-disable-next-line no-await-in-loop
await qq.write([installerBase, `bin/${config.bin}.cmd`], scripts.cmd(config));
// eslint-disable-next-line no-await-in-loop
await qq.write([installerBase, `bin/${config.bin}`], scripts.sh(config));
// eslint-disable-next-line no-await-in-loop
await qq.write([installerBase, `${config.bin}.nsi`], scripts.nsis(config, arch));
// eslint-disable-next-line no-await-in-loop
await qq.mv(buildConfig.workspace({ platform: 'win32', arch }), [installerBase, 'client']);
// eslint-disable-next-line no-await-in-loop
await qq.x(`makensis ${installerBase}/${config.bin}.nsi | grep -v "\\[compress\\]" | grep -v "^File: Descending to"`);
const o = buildConfig.dist(`win/${config.bin}-v${buildConfig.version}-${arch}.exe`);
// eslint-disable-next-line no-await-in-loop
await qq.mv([installerBase, 'installer.exe'], o);
this.log(`built ${o}`);
}
}
async checkForNSIS() {
try {
await qq.x('makensis', { stdio: [0, null, 2] });
}
catch (error) {
if (error.code === 1)
return;
if (error.code === 127)
this.error('install makensis');
else
throw error;
}
}
}
exports.default = PackWin;
PackWin.description = 'create windows installer from oclif CLI';
PackWin.flags = {
root: command_1.flags.string({ char: 'r', description: 'path to oclif CLI root', default: '.', required: true }),
};