diff --git a/Gruntfile.js b/Gruntfile.js index f9ed59621..282c8e0fb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,6 +8,21 @@ module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), exec: { + get_nwjs_for_pkg: { + command: 'if [ ! -d ./cache/0.19.5-pkg/osx64/nwjs.app ]; then cd ./cache; curl https://dl.nwjs.io/v0.19.5-mas-beta/nwjs-mas-v0.19.5-osx-x64.zip --output nwjs.zip; unzip nwjs.zip; mkdir -p ./0.19.5-pkg/osx64; cp -R ./nwjs-mas-v0.19.5-osx-x64/nwjs.app ./0.19.5-pkg/osx64/; fi' + }, + create_others_dist: { + command: 'sh webkitbuilds/create-others-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"' + }, + create_dmg_dist: { + command: 'sh webkitbuilds/create-dmg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"' + }, + create_pkg_dist: { + command: 'sh webkitbuilds/create-pkg-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>" "<%= pkg.nameCaseNoSpace %>" "<%= pkg.title %>"' + }, + sign_desktop_dist: { + command: 'sh webkitbuilds/sign-desktop-dist.sh "<%= pkg.name %>" "<%= pkg.fullVersion %>"' + }, appConfig: { command: 'node ./util/buildAppConfig.js' }, @@ -20,9 +35,6 @@ module.exports = function(grunt) { cordovaclean: { command: 'make -C cordova clean' }, - macos: { - command: 'sh webkitbuilds/build-macos.sh sign' - }, coveralls: { command: 'cat coverage/report-lcov/lcov.info |./node_modules/coveralls/bin/coveralls.js' }, @@ -61,7 +73,7 @@ module.exports = function(grunt) { stdin: true, }, desktopsign: { - cmd: 'gpg -u E0AE67E7 --output webkitbuilds/<%= pkg.title %>-linux.zip.sig --detach-sig webkitbuilds/<%= pkg.title %>-linux.zip ; gpg -u E0AE67E7 --output webkitbuilds/<%= pkg.title %>.exe.sig --detach-sig webkitbuilds/<%= pkg.title %>.exe' + cmd: 'gpg -u E0AE67E7 --output webkitbuilds/others/<%= pkg.title %>-linux.zip.sig --detach-sig webkitbuilds/others/<%= pkg.title %>-linux.zip ; gpg -u E0AE67E7 --output webkitbuilds/others/<%= pkg.title %>.exe.sig --detach-sig webkitbuilds/others/<%= pkg.title %>.exe' }, desktopverify: { cmd: 'gpg --verify webkitbuilds/<%= pkg.title %>-linux.zip.sig webkitbuilds/<%= pkg.title %>-linux.zip; gpg --verify webkitbuilds/<%= pkg.title %>.exe.sig webkitbuilds/<%= pkg.title %>.exe' @@ -233,38 +245,78 @@ module.exports = function(grunt) { expand: true, cwd: 'webkitbuilds/', src: ['.desktop', '../www/img/app/favicon.ico', '../resources/<%= pkg.name %>/linux/512x512.png'], - dest: 'webkitbuilds/<%= pkg.title %>/linux64/', + dest: 'webkitbuilds/others/<%= pkg.title %>/linux64/', flatten: true, filter: 'isFile' }], } }, nwjs: { - options: { - appName: '<%= pkg.title %>', - platforms: ['win64', 'osx64', 'linux64'], - buildDir: './webkitbuilds', - version: '0.19.5', - macIcns: './resources/<%= pkg.name %>/mac/app.icns', - exeIco: './www/img/app/logo.ico', - macPlist: { - 'CFBundleURLTypes': [ - { - 'CFBundleURLName': 'URI Handler', - 'CFBundleURLSchemes': ['bitcoin', '<%= pkg.name %>'] - } - ] - } + others: { + options: { + appName: '<%= pkg.nameCaseNoSpace %>', + platforms: ['win64', 'linux64'], + buildDir: './webkitbuilds/others', + version: '0.19.5', + exeIco: './www/img/app/logo.ico' + }, + src: ['./package.json', './www/**/*'] + }, + dmg: { + options: { + appName: '<%= pkg.nameCaseNoSpace %>', + platforms: ['osx64'], + buildDir: './webkitbuilds/dmg', + version: '0.19.5', + macIcns: './resources/<%= pkg.name %>/mac/app.icns', + exeIco: './www/img/app/logo.ico', + macPlist: { + 'CFBundleDisplayName': '<%= pkg.title %>', + 'CFBundleShortVersionString': '<%= pkg.version %>', + 'CFBundleVersion': '<%= pkg.androidVersion %>', + 'LSApplicationCategoryType': 'public.app-category.finance', + 'CFBundleURLTypes': [ + { + 'CFBundleURLName': 'URI Handler', + 'CFBundleURLSchemes': ['bitcoin', '<%= pkg.name %>'] + } + ] + } + }, + src: ['./package.json', './www/**/*'] + }, + pkg: { + options: { + appName: '<%= pkg.nameCaseNoSpace %>', + platforms: ['osx64'], + buildDir: './webkitbuilds/pkg', + version: '0.19.5', + macIcns: './resources/<%= pkg.name %>/mac/pkg/app.icns', + exeIco: './www/img/app/logo.ico', + macPlist: { + 'CFBundleIdentifier': 'com.bitcoin.mwallet.mac', + 'CFBundleDisplayName': '<%= pkg.title %>', + 'CFBundleShortVersionString': '<%= pkg.version %>', + 'CFBundleVersion': '<%= pkg.androidVersion %>', + 'LSApplicationCategoryType': 'public.app-category.finance', + 'CFBundleURLTypes': [ + { + 'CFBundleURLName': 'URI Handler', + 'CFBundleURLSchemes': ['bitcoin', '<%= pkg.name %>'] + } + ] + } + }, + src: ['./package.json', './www/**/*'] }, - src: ['./package.json', './www/**/*'] }, compress: { linux: { options: { - archive: './webkitbuilds/<%= pkg.title %>-linux.zip' + archive: './webkitbuilds/others/<%= pkg.title %>-linux.zip' }, expand: true, - cwd: './webkitbuilds/<%= pkg.title %>/linux64/', + cwd: './webkitbuilds/others/<%= pkg.title %>/linux64/', src: ['**/*'], dest: '<%= pkg.title %>-linux/' } @@ -283,9 +335,6 @@ module.exports = function(grunt) { grunt.registerTask('default', ['nggettext_compile', 'exec:appConfig', 'exec:externalServices', 'browserify', 'sass', 'concat', 'copy:ionic_fonts', 'copy:ionic_js']); grunt.registerTask('prod', ['default', 'uglify']); grunt.registerTask('translate', ['nggettext_extract']); - grunt.registerTask('desktop', ['prod', 'nwjs', 'copy:linux', 'compress:linux']); - grunt.registerTask('osx', ['prod', 'nwjs', 'exec:macos', 'exec:osxsign']); - grunt.registerTask('osx-debug', ['default', 'nwjs']); grunt.registerTask('chrome', ['default','exec:chrome']); grunt.registerTask('wp', ['prod', 'exec:wp']); grunt.registerTask('wp-copy', ['default', 'exec:wpcopy']); @@ -297,6 +346,23 @@ module.exports = function(grunt) { grunt.registerTask('android-debug', ['exec:androiddebug', 'exec:androidrun']); grunt.registerTask('android', ['exec:android']); grunt.registerTask('android-release', ['prod', 'exec:android', 'exec:androidsign']); - grunt.registerTask('desktopsign', ['exec:desktopsign', 'exec:desktopverify']); + grunt.registerTask('desktopsign', ['exec:desktopsign', 'exec:desktopverify']); + // Build desktop + grunt.registerTask('desktop-build', ['desktop-others', 'desktop-osx-dmg', 'desktop-osx-pkg']); + + // Build desktop win64 & linux64 + grunt.registerTask('desktop-others', ['prod', 'nwjs:others', 'copy:linux', 'exec:create_others_dist']); + + // Build desktop osx pkg + grunt.registerTask('desktop-osx-pkg', ['prod', 'exec:get_nwjs_for_pkg', 'nwjs:pkg', 'exec:create_pkg_dist']); + + // Build desktop osx dmg + grunt.registerTask('desktop-osx-dmg', ['prod', 'nwjs:dmg', 'exec:create_dmg_dist']); + + // Sign desktop + grunt.registerTask('desktop-sign', ['exec:sign_desktop_dist']); + + // Release desktop + grunt.registerTask('desktop-release', ['desktop-build', 'desktop-sign']); }; diff --git a/app-template/apply.js b/app-template/apply.js index 1aaee94de..143cf57a8 100755 --- a/app-template/apply.js +++ b/app-template/apply.js @@ -11,7 +11,10 @@ var templates = { 'ionic.config.json': '/', '.desktop': 'webkitbuilds/', 'setup-win.iss': 'webkitbuilds/', - 'build-macos.sh': 'webkitbuilds/', + 'create-dmg-dist.sh': 'webkitbuilds/', + 'create-others-dist.sh': 'webkitbuilds/', + 'create-pkg-dist.sh': 'webkitbuilds/', + 'sign-desktop-dist.sh': 'webkitbuilds/', 'manifest.json': 'chrome-app/', // 'bower.json': '/', }; diff --git a/app-template/bitcoincom/appConfig.json b/app-template/bitcoincom/appConfig.json index 7fba677c4..2e9f82d29 100644 --- a/app-template/bitcoincom/appConfig.json +++ b/app-template/bitcoincom/appConfig.json @@ -2,7 +2,7 @@ "packageName": "bitcoin.com", "packageDescription": "Bitcoin.com Wallet", "packageNameId": "com.bitcoin.mwallet", - "userVisibleName": "Bitcoin.com", + "userVisibleName": "Bitcoin.com Wallet", "purposeLine": "Bitcoin.com Wallet", "bundleName": "bitcoincom", "appUri": "bitcoincom", @@ -18,7 +18,7 @@ "appDescription": "Bitcoin.com Wallet", "winAppName": "BitcoinWallet", "WindowsStoreIdentityName": "18C7659D.Bitcoin.com-SecureBitcoinWallet", - "WindowsStoreDisplayName": "Bitcoin.com - Secure Bitcoin Wallet", + "WindowsStoreDisplayName": "Bitcoin.com Wallet", "wpPublisherId": "{31cdd08b-457c-413d-b440-f6665eec847d}", "wpProductId": "{5381aa50-9069-11e4-84cc-293caf9cbdc8}", "windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c", diff --git a/app-template/build-macos.sh b/app-template/create-dmg-dist.sh old mode 100755 new mode 100644 similarity index 84% rename from app-template/build-macos.sh rename to app-template/create-dmg-dist.sh index 5f09bd0b2..5b004e66a --- a/app-template/build-macos.sh +++ b/app-template/create-dmg-dist.sh @@ -1,11 +1,5 @@ #!/bin/bash -SHOULD_SIGN=$1 -if [ "$SHOULD_SIGN" ] -then - echo "Will sign the APP" -fi - # by Andy Maloney # http://asmaloney.com/2013/07/howto/packaging-a-mac-os-x-application-using-a-dmg/ @@ -16,21 +10,25 @@ if [ -d "$dir" ]; then fi # set up your app name, architecture, and background image file name -APP_NAME="*USERVISIBLENAME*" +APP_PACKAGE=$1 +APP_VERSION=$2 +APP_NAME=$3 +APP_FULLNAME=$4 + rm dmg-background.tiff -ln -s ../resources/*PACKAGENAME*/mac/dmg-background.tiff dmg-background.tiff +ln -s ../resources/bitcoin.com/mac/dmg-background.tiff dmg-background.tiff rm volume-icon.icns -ln -s ../resources/*PACKAGENAME*/mac/volume-icon.icns volume-icon.icns +ln -s ../resources/bitcoin.com/mac/volume-icon.icns volume-icon.icns DMG_VOLUME_ICON="volume-icon.icns" DMG_BACKGROUND_IMG="dmg-background.tiff" -PATH_NAME="${APP_NAME}/osx64/" +PATH_NAME="dmg/${APP_NAME}/osx64/" # you should not need to change these APP_EXE="${PATH_NAME}${APP_NAME}.app/Contents/MacOS/nwjs" VOL_NAME="${APP_NAME}" -DMG_TMP="${VOL_NAME}-temp.dmg" -DMG_FINAL="${VOL_NAME}.dmg" +DMG_TMP="dmg/${VOL_NAME}-temp.dmg" +DMG_FINAL="dmg/${VOL_NAME}.dmg" STAGING_DIR="tmp" # Check the background image DPI and convert it if it isn't 72x72 @@ -66,25 +64,6 @@ SIZE=250 if [ $? -ne 0 ]; then echo "Error: Cannot compute size of staging dir" exit - fi - -# Sign Code (MATIAS) -if [ $SHOULD_SIGN ] -then - echo "Signing ${APP_NAME} DMG" - - export IDENTITY="3rd Party Mac Developer Application: BitPay, Inc. (884JRH5R93)" - - # not need for 'out of app store' distribution (?) -# export PARENT_PLIST=parent.plist -# export CHILD_PLIST=child.plist - export APP_PATH=${STAGING_DIR}/${APP_NAME}.app - - codesign --deep -s "${IDENTITY}" $APP_PATH"/Contents/Versions/52.0.2743.82/nwjs Helper.app" && echo "Sign 1" - codesign --deep -s "${IDENTITY}" $APP_PATH"/Contents/Versions/52.0.2743.82/nwjs Framework.framework/Resources/app_mode_loader.app" && echo "Sign 2" - codesign --deep -s "${IDENTITY}" $APP_PATH && echo "Sign 3" - echo "Signing Done" - fi # create the temp DMG file @@ -175,6 +154,14 @@ hdiutil detach "${DEVICE}" echo "Creating compressed image" hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FINAL}" +export DIST_PATH="dist" + +if [ ! -d $DIST_PATH ]; then + mkdir $DIST_PATH +fi + +cp -vR "${DMG_FINAL}" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg" + # clean up rm -rf "${DMG_TMP}" rm -rf "${STAGING_DIR}" diff --git a/app-template/create-others-dist.sh b/app-template/create-others-dist.sh new file mode 100644 index 000000000..c9244b3ba --- /dev/null +++ b/app-template/create-others-dist.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# make sure we are in the correct dir when we double-click a .command file +dir=${0%/*} +if [ -d "$dir" ]; then + cd "$dir" +fi + +# set up your app name, architecture, and background image file name +APP_PACKAGE=$1 +APP_VERSION=$2 +APP_NAME=$3 +APP_FULLNAME=$4 + +export APP_LINUX_PATH="others/${APP_NAME}/linux64" +export APP_WIN_PATH="others/${APP_NAME}/win64" +export DIST_PATH="dist" + +if [ ! -d $DIST_PATH ]; then + mkdir $DIST_PATH +fi + +## +# LINUX + +echo "Building Linux..." + +# Building package +cp -R $APP_LINUX_PATH bitcoin-com-wallet +tar -cvzf "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz" bitcoin-com-wallet + +# Clean +rm -R bitcoin-com-wallet + +echo "Linux Done." + + +## +# WINDOWS + +echo "Building Windows..." + +# Building package +cp -R $APP_WIN_PATH bitcoin-com-wallet +zip -r "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip" bitcoin-com-wallet + +# Clean +rm -R bitcoin-com-wallet + +echo "Windows Done." + +echo "Done." + +exit diff --git a/app-template/create-pkg-dist.sh b/app-template/create-pkg-dist.sh new file mode 100644 index 000000000..c0b4d266d --- /dev/null +++ b/app-template/create-pkg-dist.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# make sure we are in the correct dir when we double-click a .command file +dir=${0%/*} +if [ -d "$dir" ]; then + cd "$dir" +fi + +# set up your app name, architecture, and background image file name +APP_PACKAGE=$1 +APP_VERSION=$2 +APP_NAME=$3 +APP_FULLNAME=$4 + +rm entitlements-child.plist +ln -s ../resources/bitcoin.com/mac/pkg/entitlements-child.plist entitlements-child.plist + +rm entitlements-parent.plist +ln -s ../resources/bitcoin.com/mac/pkg/entitlements-parent.plist entitlements-parent.plist + +rm build.cfg +ln -s ../resources/bitcoin.com/mac/pkg/build.cfg build.cfg + +rm build_mas.py +ln -s ../resources/bitcoin.com/mac/pkg/build_mas.py build_mas.py + +echo "Signing ${APP_NAME}" +export APP_PATH="pkg/${APP_NAME}/osx64/${APP_NAME}" +export TMP_PATH="tmp" +export DIST_PATH="dist" + +rm -rf $TMP_PATH +mkdir $TMP_PATH + +if [ ! -d $DIST_PATH ]; then + mkdir $DIST_PATH +fi + +python build_mas.py -C build.cfg -O "${TMP_PATH}/${APP_NAME}.app" -I "${APP_PATH}.app" -P "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg" + +echo "Signing Done" + +echo "Done." + +exit diff --git a/app-template/package-template.json b/app-template/package-template.json index d679b0b24..232ac7e1f 100644 --- a/app-template/package-template.json +++ b/app-template/package-template.json @@ -3,6 +3,7 @@ "description": "*DESCRIPTION*", "author": "BitPay", "version": "*VERSION*", + "androidVersion": "*ANDROIDVERSION*", "fullVersion": "*FULLVERSION*", "keywords": [ "bitcoin", @@ -14,8 +15,9 @@ ], "main": "www/index.html", "title": "*USERVISIBLENAME*", + "nameCaseNoSpace": "*NAMECASENOSPACE*", "window": { - "title": "*USERVISIBLENAME* - *PURPOSELINE*", + "title": "*USERVISIBLENAME*", "icon": "www/img/app/icon.png", "toolbar": false, "show": true, @@ -69,6 +71,8 @@ "grunt-angular-gettext": "^2.2.3", "grunt-browserify": "^5.0.0", "grunt-cli": "^1.2.0", + "grunt-curl": "^2.4.1", + "grunt-zip": "^0.17.1", "grunt-contrib-compress": "^1.3.0", "grunt-contrib-concat": "^1.0.1", "grunt-contrib-copy": "^1.0.0", @@ -99,15 +103,19 @@ "build:ios-release": "cordova prepare ios && cordova build ios --release", "build:android-release": "cordova prepare android && cordova build android --release", "build:windows-release": "cordova prepare windows && cordova build windows --release --arch=\"ARM\"", - "build:desktop": "grunt desktop", - "build:osx": "grunt osx", + "build:desktop-release": "grunt desktop-release", + "build:desktop": "grunt desktop-build", + "build:osx-pkg": "grunt desktop-osx-pkg", + "build:osx-dmg": "grunt desktop-osx-dmg", + "build:others": "grunt desktop-others", + "sign:desktop": "grunt desktop-sign", "open:ios": "open platforms/ios/*.xcodeproj", "open:android": "open -a open -a /Applications/Android\\ Studio.app platforms/android", "final:www": "npm run build:www-release", "final:ios": "npm run final:www && npm run build:ios-release && npm run open:ios", "final:android": "npm run final:www && npm run build:android-release && npm run sign:android && npm run run:android-release", "final:windows": "npm run final:www && npm run build:windows-release", - "final:desktop": "npm run build:desktop && npm run build:osx", + "final:desktop": "npm run final:www && npm run build:desktop-release", "run:android": "cordova run android --device", "run:android-release": "cordova run android --device --release", "log:android": "adb logcat | grep chromium", diff --git a/app-template/sign-desktop-dist.sh b/app-template/sign-desktop-dist.sh new file mode 100644 index 000000000..e1e5c603c --- /dev/null +++ b/app-template/sign-desktop-dist.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# make sure we are in the correct dir when we double-click a .command file +dir=${0%/*} +if [ -d "$dir" ]; then + cd "$dir" +fi + +APP_PACKAGE=$1 +APP_VERSION=$2 +export DIST_PATH="dist" + +## +# INIT GPG (YOU NEED THE PRIVATE KEY CONNECTED TO YOUR DESKTOP) +# gpg --card-edit + +## +# LINUX + +# Sig tar.gz +gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz" +gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-linux-x64.tar.gz" + +## +# WINDOWS + +# Sig zip +gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip" +gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-win-x64.zip" + +## +# OSX + +# Sig dmg +gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg" +gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.dmg" + +# Sig pkg +gpg --yes --output "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg.sig" --detach-sig "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg" +gpg --verify "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg.sig" "$DIST_PATH/${APP_PACKAGE}-wallet-${APP_VERSION}-osx.pkg" \ No newline at end of file diff --git a/resources/bitcoin.com/mac/pkg/app.icns b/resources/bitcoin.com/mac/pkg/app.icns new file mode 100644 index 000000000..40aa3ea77 Binary files /dev/null and b/resources/bitcoin.com/mac/pkg/app.icns differ diff --git a/resources/bitcoin.com/mac/pkg/build.cfg b/resources/bitcoin.com/mac/pkg/build.cfg new file mode 100644 index 000000000..3594a2688 --- /dev/null +++ b/resources/bitcoin.com/mac/pkg/build.cfg @@ -0,0 +1,32 @@ +[Sign] +## [REQUIRED] Your Application Certificate Identity +ApplicationIdentity = 3rd Party Mac Developer Application: Saint Bitts LLC (299HJ3G3BP) +## [OPTIONAL] Parent entitlements file +ParentEntitlements = entitlements-parent.plist +## [OPTIONAL] Child entitlements file +ChildEntitlements = entitlements-child.plist +## [OPTIONAL] Sandbox. Default: Yes +Sandbox = Yes + +[Package] +## [REQUIRED for --pkg] Your Installer Certificate Identity +InstallerIdentity = 3rd Party Mac Developer Installer: Saint Bitts LLC (299HJ3G3BP) +## [OPTIONAL for --pkg] Installation path +InstallPath = /Applications + +[Info.plist] +## [OPTIONAL] Your app bundle identifier +CFBundleIdentifier = com.bitcoin.mwallet.mac +## [REQUIRED] Team ID obtained from Apple Developer -> Membership -> Team ID +NWTeamID = 299HJ3G3BP +## Properties of Info.plist will be overwritten in this section. + +[Resources] +## [OPTIONAL] Your custom icon file +Icon = ../resources/bitcoin.com/mac/pkg/app.icns +## [OPTIONAL] Locales +## If Locales is not set, all current locales are preserved. +## If comma separated locale list (e.g. en,fr,zh_CN) is given, you should have +## additional [Locale locale_name] section for each locale containing localized strings. +## Locales not in the list will be removed. +Locales = en \ No newline at end of file diff --git a/resources/bitcoin.com/mac/pkg/build_mas.py b/resources/bitcoin.com/mac/pkg/build_mas.py new file mode 100755 index 000000000..d067abacd --- /dev/null +++ b/resources/bitcoin.com/mac/pkg/build_mas.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python + +import argparse +import ConfigParser +import shutil +import os +import fnmatch +import plistlib +import tempfile +from datetime import datetime +import sys +import io + +bundleid = None +verbose = False + +def info(msg): + global verbose + if verbose: + print '[INFO] %s' % msg + +def error(msg): + print '[ERROR] %s' % msg + print '\nFailed.' + sys.exit(1) + +def system(cmd): + info(cmd) + os.system(cmd) + +def check_options(config, section, required_options, msg): + missed_options = [] + + for option in required_options: + if not config.has_option(section, option): + missed_options.append(option) + + if len(missed_options) != 0: + error(msg % (section, ', '.join(missed_options))) + +def glob(pathname, pattern, returnOnFound=False): + matches = [] + for root, dirnames, filenames in os.walk(pathname): + for dirname in fnmatch.filter(dirnames, pattern): + if returnOnFound: + return os.path.join(root, dirname) + matches.append(os.path.join(root, dirname)) + for filename in fnmatch.filter(filenames, pattern): + if returnOnFound: + return os.path.join(root, filename) + matches.append(os.path.join(root, filename)) + return matches + +def get_bundle_id(args): + global bundleid + if bundleid is None: + plist = plistlib.readPlist(os.path.join(args.output, 'Contents/Info.plist')) + bundleid = plist['CFBundleIdentifier'] + return bundleid + +def get_from_info_plist(args, key, default=None): + plist = plistlib.readPlist(os.path.join(args.output, 'Contents/Info.plist')) + if key in plist: + return plist[key] + else: + return default + +def patch_info_plist_file(file, replaces): + plist = plistlib.readPlist(file) + for (key, val) in replaces: + plist[key] = val + plistlib.writePlist(plist, file) + +def generate_infoplist_strings_file(file, items): + with io.open(file, 'w', encoding='utf-16') as fd: + for item in items: + fd.write(unicode('%s = "%s";\n' % item, 'utf-8')) + +def read_config(args): + print '\nParsing config file %s' % args.config_file + if not os.path.isfile(args.config_file): + error('%s does not exist' % args.config_file) + config = ConfigParser.SafeConfigParser() + config.optionxform = str # set to str to prevent transforming into lower cases + config.read(args.config_file) + check_options(config, 'Sign', ['ApplicationIdentity'], 'Missed options in [%s]: %s') + if args.pkg: + check_options(config, 'Package', ['InstallerIdentity'], 'Missed options for --pkg in [%s]: %s') + return config + +def copy_to_output(args): + print '\nCopying %s to %s' % (args.input, args.output) + shutil.rmtree(args.output, ignore_errors=True) + shutil.copytree(args.input, args.output, symlinks=True) # symblic links are required + +def patch_info_plist(config, args): + print '\nPatching Info.plist files' + + replaces = [] + for (key, val) in config.items('Info.plist'): + replaces.append((key, val)) + + file = os.path.join(args.output, 'Contents/Info.plist') + info(file) + patch_info_plist_file(file, replaces) + + info_plist_files = glob(os.path.join(args.output, 'Contents/Versions'), 'Info.plist') + for file in info_plist_files: + if 'nwjs Framework' in file: + tmp_replaces = [('CFBundleIdentifier', '%s.framework' % get_bundle_id(args))] + elif 'nwjs Helper' in file: + tmp_replaces = [('CFBundleIdentifier', '%s.helper' % get_bundle_id(args))] + else: + error('Cannot patch unknown Info.plist %s' % file) + info(file) + patch_info_plist_file(file, tmp_replaces) + +def patch_locales(config, args): + print '\nPatching locales' + locales = config.get('Resources', 'Locales').split(',') + removed_locales = [] + generated_locales = [] + for infoplist_strings_file in glob(os.path.join(args.output, 'Contents/Resources'), 'InfoPlist.strings'): + locale_dir = os.path.dirname(infoplist_strings_file) + (locale, _) = os.path.splitext(os.path.basename(locale_dir)) + if locale not in locales: + removed_locales.append(locale) + shutil.rmtree(locale_dir) + elif config.has_section('Locale %s' % locale): + generated_locales.append(locale) + generate_infoplist_strings_file(infoplist_strings_file, config.items('Locale %s' % locale)) + else: + error('Missing [Locale %s] section' % locale) + + if len(generated_locales) > 0: + info('Generated locales for %s' % ', '.join(generated_locales)) + if len(removed_locales) > 0: + info('Removed locales for %s' % ', '.join(removed_locales)) + + removed_paks = [] + for local_pak in glob(os.path.join(args.output, 'Contents/Versions'), 'locale.pak'): + locale_dir = os.path.dirname(local_pak) + (locale, _) = os.path.splitext(os.path.basename(locale_dir)) + if locale != 'en' and locale not in locales: + removed_paks.append(locale) + shutil.rmtree(locale_dir) + + if len(removed_paks) > 0: + info('Removed .pak files for %s' % ', '.join(removed_locales)) + +def patch_icon(config, args): + plist = plistlib.readPlist(os.path.join(args.output, 'Contents/Info.plist')) + icon = os.path.join(os.path.dirname(args.config_file), config.get('Resources', 'Icon')) + dest_icon = os.path.join(args.output, 'Contents/Resources/%s' % plist['CFBundleIconFile']) + info('Copying icon from %s to %s' % (icon, dest_icon)) + shutil.copy2(icon, dest_icon) + +def codesign_app(config, args): + print '\nCodesigning' + + bundleid = get_bundle_id(args) + + identity = config.get('Sign', 'ApplicationIdentity') + sandbox = True + if config.has_option('Sign', 'Sandbox'): + sandbox = config.getboolean('Sign', 'Sandbox') + + ## sign child frameworks and helpers + (_, tmp_child_entitlements) = tempfile.mkstemp() + if config.has_option('Sign', 'ChildEntitlements'): + child = config.get('Sign', 'ChildEntitlements') + child_entitlements = plistlib.readPlist(child) + else: + child_entitlements = { + 'com.apple.security.app-sandbox' : sandbox, + 'com.apple.security.inherit' : True + } + + plistlib.writePlist(child_entitlements, tmp_child_entitlements) + info('Child entitlements: %s' % tmp_child_entitlements) + framework = glob(args.output, 'nwjs Framework.framework', returnOnFound=True) + system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, framework)) + helperApp = glob(args.output, 'nwjs Helper.app', returnOnFound=True) + system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_child_entitlements, helperApp)) + + ## sign parent app + (_, tmp_parent_entitlements) = tempfile.mkstemp() + if config.has_option('Sign', 'ParentEntitlements'): + parent = config.get('Sign', 'ParentEntitlements') + parent_entitlements = plistlib.readPlist(parent) + else: + parent_entitlements = {} + teamid = get_from_info_plist(args, 'NWTeamID', default=None) + if teamid is None: + groupid = bundleid + else: + groupid = '%s.%s' % (teamid, bundleid) + parent_entitlements['com.apple.security.app-sandbox'] = sandbox + parent_entitlements['com.apple.security.application-groups'] = [groupid] + plistlib.writePlist(parent_entitlements, tmp_parent_entitlements) + + info('Parent entitlements: %s' % tmp_parent_entitlements) + system('codesign -f --verbose -s "%s" --entitlements %s --deep "%s"' % (identity, tmp_parent_entitlements, args.output)) + +def productbuild(config, args): + print '\nRunning productbuild' + installer_identity = config.get('Package', 'InstallerIdentity') + if config.has_option('Package', 'InstallPath'): + install_path = config.get('Package', 'InstallPath') + else: + install_path = '/Applications' + system('productbuild --component "%s" "%s" --sign "%s" "%s"' % (args.output, install_path, installer_identity, args.pkg)) + +def main(): + parser = argparse.ArgumentParser(description='Signing tool for NW.js app') + parser.add_argument('-C', '--config-file', default='build.cfg', help='config file. (default: build.cfg)') + parser.add_argument('-I', '--input', default='nwjs.app', help='path to input app. (default: nwjs.app)') + parser.add_argument('-O', '--output', default='nwjs_output.app', help='path to output app. (default: nwjs_output.app)') + parser.add_argument('-S', '--skip-patching', default=False, help='run codesign without patching the app. (default: False)', action='store_true') + parser.add_argument('-P', '--pkg', default=None, help='run productbuild to generate .pkg after codesign. (default: None)') + parser.add_argument('-V', '--verbose', default=False, help='display detailed information. (default: False)', action='store_true') + args = parser.parse_args() + + global verbose + verbose = args.verbose + + # read config file + config = read_config(args) + + # make a copy + copy_to_output(args) + + if not args.skip_patching: + # patch Info.plist + patch_info_plist(config, args) + + # process resources & locales + if config.has_section('Resources'): + if config.has_option('Resources', 'Locales'): + patch_locales(config, args) + if config.has_option('Resources', 'Icon'): + patch_icon(config, args) + + # codesign + codesign_app(config, args) + + if args.pkg: + productbuild(config, args) + + print '\nDone.' + +if __name__ == "__main__": + main() diff --git a/resources/bitcoin.com/mac/pkg/entitlements-child.plist b/resources/bitcoin.com/mac/pkg/entitlements-child.plist new file mode 100644 index 000000000..635e25aac --- /dev/null +++ b/resources/bitcoin.com/mac/pkg/entitlements-child.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + \ No newline at end of file diff --git a/resources/bitcoin.com/mac/pkg/entitlements-parent.plist b/resources/bitcoin.com/mac/pkg/entitlements-parent.plist new file mode 100644 index 000000000..12d6997e3 --- /dev/null +++ b/resources/bitcoin.com/mac/pkg/entitlements-parent.plist @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + $GROUPID + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + com.apple.security.device.camera + + + diff --git a/www/index.html b/www/index.html index 76827f685..47dc27d2a 100644 --- a/www/index.html +++ b/www/index.html @@ -11,7 +11,7 @@ - Bitcoin.com - Bitcoin.com Wallet + Bitcoin.com Wallet