From b9b8592a07fd37689a7a2c0372f7350194cd2637 Mon Sep 17 00:00:00 2001 From: LeopoldTal Date: Sun, 27 Sep 2020 23:14:43 +0200 Subject: [PATCH] Build, sign, notarise, & upload on OS X (#687) * sign & notarise darwin package * upload bundle as github release * allow unsigned build and full build with release * deref darwin bundle symlinks only when building on win32 Windows [mangles symlinks](https://github.com/electron/electron-packager/issues/71). Currently we work around this by placing several copies of the frameworks in OS X app bundles (see 1e5aa3867d0681a66598879acf8c6322c0776184). However: - This is invalid: the framework toplevel must [only contain symlinks](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html). `codesign` [refuses to sign](https://stackoverflow.com/questions/25969946/osx-10-9-5-code-signing-v2-signing-a-framework-with-bundle-format-is-ambiguou) this invalid structure. - It seriously bloats the bundle. Since there's no fix for the Windows misbehaviour, keep the workaround, but only when cross-building on win32 for darwin; and log a warning. --- gulp/buildutils.js | 8 +++ gulp/entitlements.plist | 12 ++++ gulp/gulpfile.js | 18 ++++++ gulp/release-uploader.js | 66 ++++++++++++++++++++ gulp/standalone.js | 25 +++++++- package.json | 1 + yarn.lock | 127 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 gulp/entitlements.plist create mode 100644 gulp/release-uploader.js diff --git a/gulp/buildutils.js b/gulp/buildutils.js index 041c8b1d..ea253773 100644 --- a/gulp/buildutils.js +++ b/gulp/buildutils.js @@ -25,6 +25,14 @@ module.exports = { }); }, + getTag() { + try { + return execSync("git describe --tag --exact-match").toString("ascii"); + } catch (e) { + throw new Error('Current git HEAD is not a version tag'); + } + }, + getVersion() { return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString()); }, diff --git a/gulp/entitlements.plist b/gulp/entitlements.plist new file mode 100644 index 00000000..8f574f5f --- /dev/null +++ b/gulp/entitlements.plist @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.debugger + + + diff --git a/gulp/gulpfile.js b/gulp/gulpfile.js index 3e03fcc7..6af84223 100644 --- a/gulp/gulpfile.js +++ b/gulp/gulpfile.js @@ -42,6 +42,10 @@ const envVars = [ "SHAPEZ_CLI_STAGING_FTP_PW", "SHAPEZ_CLI_LIVE_FTP_USER", "SHAPEZ_CLI_LIVE_FTP_PW", + "SHAPEZ_CLI_APPLE_ID", + "SHAPEZ_CLI_APPLE_CERT_NAME", + "SHAPEZ_CLI_GITHUB_USER", + "SHAPEZ_CLI_GITHUB_TOKEN", ]; for (let i = 0; i < envVars.length; ++i) { @@ -78,6 +82,9 @@ docs.gulptasksDocs($, gulp, buildFolder); const standalone = require("./standalone"); standalone.gulptasksStandalone($, gulp, buildFolder); +const releaseUploader = require("./release-uploader"); +releaseUploader.gulptasksReleaseUploader($, gulp, buildFolder); + const translations = require("./translations"); translations.gulptasksTranslations($, gulp, buildFolder); @@ -299,6 +306,17 @@ gulp.task( gulp.series("utils.cleanup", "step.standalone-prod.all", "step.postbuild") ); +// OS X build and release upload +gulp.task( + "build.darwin64-prod", + gulp.series( + "build.standalone-prod", + "standalone.prepare", + "standalone.package.prod.darwin64", + "standalone.uploadRelease.darwin64" + ) +); + // Deploying! gulp.task( "main.deploy.alpha", diff --git a/gulp/release-uploader.js b/gulp/release-uploader.js new file mode 100644 index 00000000..eececa4a --- /dev/null +++ b/gulp/release-uploader.js @@ -0,0 +1,66 @@ +const path = require("path"); +const fs = require("fs"); +const execSync = require("child_process").execSync; +const { Octokit } = require("@octokit/rest"); +const buildutils = require("./buildutils"); + +function gulptasksReleaseUploader($, gulp, buildFolder) { + const standaloneDir = path.join(__dirname, "..", "tmp_standalone_files"); + const darwinApp = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", "shapez.io-standalone.app"); + const dmgName = "shapez.io-standalone.dmg"; + const dmgPath = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", dmgName); + + gulp.task("standalone.uploadRelease.darwin64.cleanup", () => { + return gulp.src(dmgPath, { read: false, allowEmpty: true }).pipe($.clean({ force: true })); + }); + + gulp.task("standalone.uploadRelease.darwin64.compress", cb => { + console.log("Packaging disk image", dmgPath); + execSync(`hdiutil create -format UDBZ -srcfolder ${darwinApp} ${dmgPath}`); + cb(); + }); + + gulp.task("standalone.uploadRelease.darwin64.upload", async cb => { + const currentTag = buildutils.getTag(); + + const octokit = new Octokit({ + auth: process.env.SHAPEZ_CLI_GITHUB_TOKEN + }); + + const createdRelease = await octokit.request("POST /repos/{owner}/{repo}/releases", { + owner: process.env.SHAPEZ_CLI_GITHUB_USER, + repo: "shapez.io", + tag_name: currentTag, + name: currentTag, + draft: true + }); + + const { data: { id, upload_url } } = createdRelease; + console.log(`Created release ${id} for tag ${currentTag}`); + + const dmgContents = fs.readFileSync(dmgPath); + const dmgSize = fs.statSync(dmgPath).size; + console.log("Uploading", dmgContents.length / 1024 / 1024, "MB to", upload_url); + + await octokit.request({ + method: "POST", + url: upload_url, + headers: { + "content-type": "application/x-apple-diskimage" + }, + name: dmgName, + data: dmgContents + }); + + cb(); + }); + + gulp.task("standalone.uploadRelease.darwin64", + gulp.series( + "standalone.uploadRelease.darwin64.cleanup", + "standalone.uploadRelease.darwin64.compress", + "standalone.uploadRelease.darwin64.upload" + )); +} + +module.exports = { gulptasksReleaseUploader }; diff --git a/gulp/standalone.js b/gulp/standalone.js index 3b0112d4..5fbb4c8a 100644 --- a/gulp/standalone.js +++ b/gulp/standalone.js @@ -1,3 +1,4 @@ +require('colors'); const packager = require("electron-packager"); const path = require("path"); const { getVersion } = require("./buildutils"); @@ -80,8 +81,9 @@ function gulptasksStandalone($, gulp) { * @param {'win32'|'linux'|'darwin'} platform * @param {'x64'|'ia32'} arch * @param {function():void} cb + * @param {boolean=} isRelease */ - function packageStandalone(platform, arch, cb) { + function packageStandalone(platform, arch, cb, isRelease = true) { const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml")); packager({ @@ -99,6 +101,20 @@ function gulptasksStandalone($, gulp) { overwrite: true, appBundleId: "io.shapez.standalone", appCategoryType: "public.app-category.games", + ...(isRelease && platform === "darwin" && { + osxSign: { + identity: process.env.SHAPEZ_CLI_APPLE_CERT_NAME, + "hardened-runtime": true, + hardenedRuntime: true, + entitlements: 'entitlements.plist', + 'entitlements-inherit': 'entitlements.plist', + 'signature-flags': 'library' + }, + osxNotarize: { + appleId: process.env.SHAPEZ_CLI_APPLE_ID, + appleIdPassword: "@keychain:SHAPEZ_CLI_APPLE_ID" + } + }) }).then( appPaths => { console.log("Packages created:", appPaths); @@ -123,7 +139,11 @@ function gulptasksStandalone($, gulp) { fs.chmodSync(path.join(appPath, "play.sh"), 0o775); } - if (platform === "darwin") { + if (process.platform === "win32" && platform === "darwin") { + console.warn("Cross-building for macOS on Windows: dereferencing symlinks.\n".red + + "This will nearly double app size and make code signature invalid. Sorry!\n".red.bold + + "For more information, see " + "https://github.com/electron/electron-packager/issues/71".underline); + // Clear up framework folders fs.writeFileSync( path.join(appPath, "play.sh"), @@ -175,6 +195,7 @@ function gulptasksStandalone($, gulp) { gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb)); gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb)); gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb)); + gulp.task("standalone.package.prod.darwin64.unsigned", cb => packageStandalone("darwin", "x64", cb, false)); gulp.task( "standalone.package.prod", diff --git a/package.json b/package.json index bdc0eb53..acd23c07 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "yawn-yaml": "^1.5.0" }, "devDependencies": { + "@octokit/rest": "^18.0.6", "@typescript-eslint/eslint-plugin": "3.0.1", "@typescript-eslint/parser": "3.0.1", "autoprefixer": "^9.4.3", diff --git a/yarn.lock b/yarn.lock index 7fd22674..da9d4625 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1000,6 +1000,103 @@ dependencies: core-js "^2.5.7" +"@octokit/auth-token@^2.4.0": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.2.tgz#10d0ae979b100fa6b72fa0e8e63e27e6d0dbff8a" + integrity sha512-jE/lE/IKIz2v1+/P0u4fJqv0kYwXOTujKemJMFr6FeopsxlIK3+wKDCJGnysg81XID5TgZQbIfuJ5J0lnTiuyQ== + dependencies: + "@octokit/types" "^5.0.0" + +"@octokit/core@^3.0.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.1.2.tgz#c937d5f9621b764573068fcd2e5defcc872fd9cc" + integrity sha512-AInOFULmwOa7+NFi9F8DlDkm5qtZVmDQayi7TUgChE3yeIGPq0Y+6cAEXPexQ3Ea+uZy66hKEazR7DJyU+4wfw== + dependencies: + "@octokit/auth-token" "^2.4.0" + "@octokit/graphql" "^4.3.1" + "@octokit/request" "^5.4.0" + "@octokit/types" "^5.0.0" + before-after-hook "^2.1.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.6" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.6.tgz#4f09f2b468976b444742a1d5069f6fa45826d999" + integrity sha512-7Cc8olaCoL/mtquB7j/HTbPM+sY6Ebr4k2X2y4JoXpVKQ7r5xB4iGQE0IoO58wIPsUk4AzoT65AMEpymSbWTgQ== + dependencies: + "@octokit/types" "^5.0.0" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.3.1": + version "4.5.6" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.6.tgz#708143ba15cf7c1879ed6188266e7f270be805d4" + integrity sha512-Rry+unqKTa3svswT2ZAuqenpLrzJd+JTv89LTeVa5UM/5OX8o4KTkPL7/1ABq4f/ZkELb0XEK/2IEoYwykcLXg== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/plugin-paginate-rest@^2.2.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.4.0.tgz#92f951ddc8a1cd505353fa07650752ca25ed7e93" + integrity sha512-YT6Klz3LLH6/nNgi0pheJnUmTFW4kVnxGft+v8Itc41IIcjl7y1C8TatmKQBbCSuTSNFXO5pCENnqg6sjwpJhg== + dependencies: + "@octokit/types" "^5.5.0" + +"@octokit/plugin-request-log@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e" + integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw== + +"@octokit/plugin-rest-endpoint-methods@4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.0.tgz#c5a0691b3aba5d8b4ef5dffd6af3649608f167ba" + integrity sha512-1/qn1q1C1hGz6W/iEDm9DoyNoG/xdFDt78E3eZ5hHeUfJTLJgyAMdj9chL/cNBHjcjd+FH5aO1x0VCqR2RE0mw== + dependencies: + "@octokit/types" "^5.5.0" + deprecation "^2.3.1" + +"@octokit/request-error@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.2.tgz#0e76b83f5d8fdda1db99027ea5f617c2e6ba9ed0" + integrity sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw== + dependencies: + "@octokit/types" "^5.0.1" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.3.0", "@octokit/request@^5.4.0": + version "5.4.9" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.9.tgz#0a46f11b82351b3416d3157261ad9b1558c43365" + integrity sha512-CzwVvRyimIM1h2n9pLVYfTDmX9m+KHSgCpqPsY8F1NdEK8IaWqXhSBXsdjOBFZSpEcxNEeg4p0UO9cQ8EnOCLA== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.0.0" + "@octokit/types" "^5.0.0" + deprecation "^2.0.0" + is-plain-object "^5.0.0" + node-fetch "^2.6.1" + once "^1.4.0" + universal-user-agent "^6.0.0" + +"@octokit/rest@^18.0.6": + version "18.0.6" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.0.6.tgz#76c274f1a68f40741a131768ef483f041e7b98b6" + integrity sha512-ES4lZBKPJMX/yUoQjAZiyFjei9pJ4lTTfb9k7OtYoUzKPDLl/M8jiHqt6qeSauyU4eZGLw0sgP1WiQl9FYeM5w== + dependencies: + "@octokit/core" "^3.0.0" + "@octokit/plugin-paginate-rest" "^2.2.0" + "@octokit/plugin-request-log" "^1.0.0" + "@octokit/plugin-rest-endpoint-methods" "4.2.0" + +"@octokit/types@^5.0.0", "@octokit/types@^5.0.1", "@octokit/types@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" + integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== + dependencies: + "@types/node" ">= 8" + "@sindresorhus/is@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" @@ -1037,6 +1134,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/node@>= 8": + version "14.11.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" + integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== + "@types/q@^1.5.1": version "1.5.2" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" @@ -1589,6 +1691,11 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +before-after-hook@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" + integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== + bfj@^6.1.1: version "6.1.2" resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" @@ -2876,6 +2983,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -4581,6 +4693,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-png@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d" @@ -5373,6 +5490,11 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" @@ -8147,6 +8269,11 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"