macos installer

pull/1705/head
antelle 2 years ago
parent 6a34fffcc4
commit b8ce1b5cc1
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C

@ -25,9 +25,6 @@ module.exports = function (grunt) {
const dt = date.toISOString().replace(/T.*/, '');
const year = date.getFullYear();
const zipCommentPlaceholderPart = 'zip_comment_placeholder_that_will_be_replaced_with_hash';
const zipCommentPlaceholder =
zipCommentPlaceholderPart + '.'.repeat(512 - zipCommentPlaceholderPart.length);
const electronVersion = pkg.dependencies.electron.replace(/^\D/, '');
const skipSign = grunt.option('skip-sign');
const getCodeSignConfig = () =>
@ -151,7 +148,7 @@ module.exports = function (grunt) {
options: { mode: '0755' }
},
'desktop-darwin-installer-helper-x64': {
cwd: 'package/osx/KeeWeb Installer.app',
cwd: 'tmp/desktop/KeeWeb Installer.app',
src: '**',
dest:
'tmp/desktop/KeeWeb-darwin-x64/KeeWeb.app/Contents/Installer/KeeWeb Installer.app',
@ -160,7 +157,7 @@ module.exports = function (grunt) {
options: { mode: true }
},
'desktop-darwin-installer-helper-arm64': {
cwd: 'package/osx/KeeWeb Installer.app',
cwd: 'tmp/desktop/KeeWeb Installer.app',
src: '**',
dest:
'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app/Contents/Installer/KeeWeb Installer.app',
@ -242,6 +239,11 @@ module.exports = function (grunt) {
src: `tmp/desktop/electron-builder/keeweb-${pkg.version}.AppImage`,
dest: `dist/desktop/KeeWeb-${pkg.version}.linux.AppImage`,
nonull: true
},
'darwin-installer-icon': {
src: 'graphics/icon.icns',
dest: 'tmp/desktop/KeeWeb Installer.app/Contents/Resources/applet.icns',
nonull: true
}
},
eslint: {
@ -445,6 +447,16 @@ module.exports = function (grunt) {
}
}
},
osacompile: {
options: {
language: 'JavaScript'
},
installer: {
files: {
'tmp/desktop/KeeWeb Installer.app': 'package/osx/installer/main.js'
}
}
},
compress: {
options: {
level: 6
@ -595,6 +607,9 @@ module.exports = function (grunt) {
},
'desktop-arm64': {
src: 'tmp/desktop/KeeWeb-darwin-arm64/KeeWeb.app'
},
'installer': {
src: 'tmp/desktop/KeeWeb Installer.app'
}
},
notarize: {

@ -58,6 +58,7 @@ const AppRightsChecker = {
runInstaller() {
Launcher.spawn({
cmd: this.AppPath + '/Contents/Installer/KeeWeb Installer.app/Contents/MacOS/applet',
args: ['--install'],
complete: () => {
this.needRunInstaller((needRun) => {
if (this.alert && !needRun) {

@ -0,0 +1,40 @@
const { spawnSync } = require('child_process');
module.exports = function (grunt) {
grunt.registerMultiTask(
'osacompile',
'Builds an executable .app package with osacompile',
async function () {
const done = this.async();
const opt = this.options();
for (const file of this.files) {
const dest = file.dest;
const src = file.src[0];
const args = [];
if (opt.language) {
args.push('-l', opt.language);
}
args.push('-o', dest);
args.push(src);
grunt.log.writeln(`Running: osacompile ${args.join(' ')}`);
const res = spawnSync('osacompile', args);
if (res.status) {
grunt.warn(
`osacompile exit code ${
res.status
}.\nSTDOUT:\n${res.stdout.toString()}\nSTDERR:\n${res.stderr.toString()}`
);
}
grunt.log.writeln(`Built ${dest}`);
}
done();
}
);
};

@ -33,6 +33,7 @@ module.exports = function (grunt) {
grunt.registerTask('build-desktop-executables-darwin', [
'electron:darwin-x64',
'electron:darwin-arm64',
'build-darwin-installer',
'copy:desktop-darwin-helper-x64',
'copy:desktop-darwin-helper-arm64',
'copy:desktop-darwin-installer-helper-x64',
@ -45,6 +46,12 @@ module.exports = function (grunt) {
sign ? 'notarize:desktop-arm64' : 'noop'
]);
grunt.registerTask('build-darwin-installer', [
'osacompile:installer',
'copy:darwin-installer-icon',
sign ? 'osx-sign:installer' : 'noop'
]);
grunt.registerTask('build-desktop-executables-win32', [
'electron:win32-x64',
'electron:win32-ia32',

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>applet</string>
<key>CFBundleIconFile</key>
<string>applet</string>
<key>CFBundleIdentifier</key>
<string>com.apple.ScriptEditor.id.KeeWeb-Installer</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>KeeWeb Installer</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>aplt</string>
<key>LSMinimumSystemVersionByArchitecture</key>
<dict>
<key>x86_64</key>
<string>10.6</string>
</dict>
<key>LSRequiresCarbon</key>
<true/>
<key>WindowState</key>
<dict>
<key>bundleDividerCollapsed</key>
<true/>
<key>bundlePositionOfDivider</key>
<real>0.0</real>
<key>dividerCollapsed</key>
<false/>
<key>eventLogLevel</key>
<integer>2</integer>
<key>name</key>
<string>ScriptWindowState</string>
<key>positionOfDivider</key>
<real>421</real>
<key>savedFrame</key>
<string>20 90 700 672 0 0 1280 777 </string>
<key>selectedTab</key>
<string>description</string>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

@ -1,5 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf1504\cocoasubrtf830
{\fonttbl}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
}

@ -1,177 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/Scripts/main.scpt</key>
<data>
hsRwl39Q/JbFKwwQ/MLvOD3Mf4Q=
</data>
<key>Resources/applet.icns</key>
<data>
vSn/k+EzqMViG5RcXNXpodKsdyk=
</data>
<key>Resources/applet.rsrc</key>
<data>
8fN/GtxUP559toTqTl+V3wCZeoU=
</data>
<key>Resources/description.rtfd/TXT.rtf</key>
<data>
U1/ikutWmrJdAXLoU25d4KUS06c=
</data>
</dict>
<key>files2</key>
<dict>
<key>Resources/Scripts/main.scpt</key>
<dict>
<key>hash</key>
<data>
hsRwl39Q/JbFKwwQ/MLvOD3Mf4Q=
</data>
<key>hash2</key>
<data>
t7kR7hl6N/mOn8SxdBAqRDqpnWm/sWW4XJ4jYDK4U5I=
</data>
</dict>
<key>Resources/applet.icns</key>
<dict>
<key>hash</key>
<data>
vSn/k+EzqMViG5RcXNXpodKsdyk=
</data>
<key>hash2</key>
<data>
NcZgMymH9fQebOOAufbFmH8w7f0RfzDMZRykqiqlrGE=
</data>
</dict>
<key>Resources/applet.rsrc</key>
<dict>
<key>hash</key>
<data>
8fN/GtxUP559toTqTl+V3wCZeoU=
</data>
<key>hash2</key>
<data>
eBEutqxEKm3EiJlpx6YIakFWJU2RKjnLue7r2J/WmJo=
</data>
</dict>
<key>Resources/description.rtfd/TXT.rtf</key>
<dict>
<key>hash</key>
<data>
U1/ikutWmrJdAXLoU25d4KUS06c=
</data>
<key>hash2</key>
<data>
vGT9c0omY5YveJ9Gjv3lLYFVn64gjtcT7Nc6UAWGXh4=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

@ -0,0 +1,11 @@
{
"env": {
"applescript": true
},
"globals": {
"app": false
},
"rules": {
"no-console": "off"
}
}

@ -0,0 +1,15 @@
#!/bin/bash
set -euxo pipefail
rm -rf package/osx/KeeWeb\ Installer.app
osacompile \
-l JavaScript \
-o package/osx/KeeWeb\ Installer.app \
package/osx/installer/main.js
cp graphics/icon.icns package/osx/KeeWeb\ Installer.app/Contents/Resources/applet.icns
codesign --remove-signature package/osx/KeeWeb\ Installer.app
CS_ID=$(cat keys/codesign.json | jq -j '.identities.app')
codesign -s "$CS_ID" package/osx/KeeWeb\ Installer.app

@ -0,0 +1,85 @@
#!/usr/bin/env osascript -l JavaScript
ObjC.import('Foundation');
ObjC.import('stdlib');
const app = Application.currentApplication();
app.includeStandardAdditions = true;
const args = getArgs();
if (args.update) {
const waitPid = args.waitPid | 0;
if (waitPid) {
waitForExitOrKill(waitPid);
}
const dmg = checkFilePath(args.dmg, 'dmg');
const target = checkFilePath(args.target, 'app');
const targetOwner = app.doShellScript(`stat -f '%Su' ${target}`);
const setAdminRights = targetOwner === 'root';
const tmpDir = dmg + '.mount';
let script = `set -euxo pipefail
hdiutil detach ${tmpDir} 2>/dev/null || true
rm -rf ${tmpDir}
mkdir -p ${tmpDir}
hdiutil attach -readonly -nobrowse -mountpoint ${tmpDir} ${dmg}
rm -rf ${target}
cp -pR ${tmpDir}/KeeWeb.app ${target}
hdiutil detach ${tmpDir}
rm -rf ${tmpDir}
`;
const scriptOptions = {};
if (setAdminRights) {
script += `chown -R 0 ${target}`;
scriptOptions.administratorPrivileges = true;
}
app.doShellScript(script, scriptOptions);
} else if (args.install) {
app.doShellScript('chown -R 0 /Applications/KeeWeb.app', { administratorPrivileges: true });
} else {
throw 'Unknown operation';
}
function getArgs() {
// https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/Shell-and-CLI-Interactions#arguments
const objcArgs = $.NSProcessInfo.processInfo.arguments;
const argc = objcArgs.count;
const args = {};
for (let i = 0; i < argc; i++) {
const arg = ObjC.unwrap(objcArgs.objectAtIndex(i));
const match = arg.match(/^--([^=]+)(=(.*))?/);
if (match) {
const argName = match[1].replace(/-./g, (m) => m[1].toUpperCase());
args[argName] = match[3] || true;
}
}
return args;
}
function waitForExitOrKill(pid) {
for (let i = 0; i < 10; i++) {
const psCount = Application('System Events').processes.whose({ unixId: pid }).length;
if (psCount === 0) {
return;
}
delay(1);
}
app.doShellScript(`kill ${pid}`);
}
function checkFilePath(path, ext) {
if (!path) {
throw `File not specified: ${ext}`;
}
if (!path.endsWith('.' + ext)) {
throw `Bad file extension: ${ext} (${path})`;
}
const file = Application('System Events').files.byName(path);
if (!file.exists()) {
throw `File doesn't exist: ${ext} (${path})`;
}
return file.posixPath().replace(/ /g, '\\ ');
}
Loading…
Cancel
Save