diff --git a/.github/workflows/electron-build.yml b/.github/workflows/electron-build.yml index 63de08f7..b9d6c3fb 100644 --- a/.github/workflows/electron-build.yml +++ b/.github/workflows/electron-build.yml @@ -12,6 +12,7 @@ on: - all - windows - linux + - macos jobs: build-windows: @@ -91,3 +92,105 @@ jobs: name: Termix-Linux-Portable path: Termix-Linux-Portable.zip retention-days: 30 + + build-macos: + runs-on: macos-latest + if: github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'macos' || github.event.inputs.build_type == '' + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 1 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Build macOS DMG + run: npm run build:mac-dmg + env: + CSC_IDENTITY_AUTO_DISCOVERY: false + + - name: Build macOS Zip + run: npm run build:mac-zip + env: + CSC_IDENTITY_AUTO_DISCOVERY: false + + - name: Upload macOS DMG Artifact + uses: actions/upload-artifact@v4 + with: + name: Termix-macOS-DMG + path: release/*.dmg + retention-days: 30 + + - name: Upload macOS Zip Artifact + uses: actions/upload-artifact@v4 + with: + name: Termix-macOS-Zip + path: release/*.zip + retention-days: 30 + + build-macos-mas: + runs-on: macos-latest + if: github.event.inputs.build_type == 'macos' || github.event.inputs.build_type == 'all' + needs: [] + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 1 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Import Code Signing Certificates + if: github.event_name == 'workflow_dispatch' && (vars.MAC_BUILD_CERTIFICATE_BASE64 != '' || secrets.MAC_BUILD_CERTIFICATE_BASE64 != '') + env: + MAC_BUILD_CERTIFICATE_BASE64: ${{ secrets.MAC_BUILD_CERTIFICATE_BASE64 }} + MAC_P12_PASSWORD: ${{ secrets.MAC_P12_PASSWORD }} + MAC_KEYCHAIN_PASSWORD: ${{ secrets.MAC_KEYCHAIN_PASSWORD }} + run: | + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + echo -n "$MAC_BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH + + security create-keychain -p "$MAC_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$MAC_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + security import $CERTIFICATE_PATH -P "$MAC_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + - name: Build macOS App Store Package + run: npm run build:mac-mas + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + CSC_IDENTITY_AUTO_DISCOVERY: false + + - name: Upload macOS MAS Artifact + uses: actions/upload-artifact@v4 + with: + name: Termix-macOS-MAS + path: release/mas/*.pkg + retention-days: 30 + + - name: Clean up keychain + if: always() + run: | + security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist new file mode 100644 index 00000000..341d88b4 --- /dev/null +++ b/build/entitlements.mac.plist @@ -0,0 +1,20 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + + diff --git a/build/entitlements.mas.inherit.plist b/build/entitlements.mas.inherit.plist new file mode 100644 index 00000000..eb23e9ac --- /dev/null +++ b/build/entitlements.mas.inherit.plist @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/build/entitlements.mas.plist b/build/entitlements.mas.plist new file mode 100644 index 00000000..c9ac7c4a --- /dev/null +++ b/build/entitlements.mas.plist @@ -0,0 +1,20 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/build/notarize.js b/build/notarize.js new file mode 100644 index 00000000..914319e9 --- /dev/null +++ b/build/notarize.js @@ -0,0 +1,31 @@ +const { notarize } = require('@electron/notarize'); + +exports.default = async function notarizing(context) { + const { electronPlatformName, appOutDir } = context; + + if (electronPlatformName !== 'darwin') { + return; + } + + const appName = context.packager.appInfo.productFilename; + const appPath = `${appOutDir}/${appName}.app`; + + const appleId = process.env.APPLE_ID; + const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD; + const teamId = process.env.APPLE_TEAM_ID; + + if (!appleId || !appleIdPassword || !teamId) { + return; + } + + try { + await notarize({ + appPath: appPath, + appleId: appleId, + appleIdPassword: appleIdPassword, + teamId: teamId, + }); + } catch (error) { + throw error; + } +}; diff --git a/electron-builder.json b/electron-builder.json index 9fb6d36b..6cd1504f 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -58,5 +58,60 @@ "StartupWMClass": "termix" } } - } + }, + "mac": { + "target": [ + { + "target": "dmg", + "arch": ["x64", "arm64"] + }, + { + "target": "zip", + "arch": ["x64", "arm64"] + }, + { + "target": "mas", + "arch": ["x64", "arm64"] + } + ], + "icon": "public/icon.icns", + "category": "public.app-category.developer-tools", + "hardenedRuntime": true, + "gatekeeperAssess": false, + "entitlements": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.plist", + "type": "distribution", + "minimumSystemVersion": "10.15" + }, + "dmg": { + "contents": [ + { + "x": 130, + "y": 220 + }, + { + "x": 410, + "y": 220, + "type": "link", + "path": "/Applications" + } + ], + "artifactName": "${productName}-${version}-${arch}.${ext}", + "sign": false, + "writeUpdateInfo": false + }, + "mas": { + "entitlements": "build/entitlements.mas.plist", + "entitlementsInherit": "build/entitlements.mas.inherit.plist", + "hardenedRuntime": false, + "gatekeeperAssess": false, + "asarUnpack": ["**/*.node"], + "type": "distribution", + "category": "public.app-category.developer-tools", + "extendInfo": { + "ElectronTeamID": "YOUR_TEAM_ID", + "ITSAppUsesNonExemptEncryption": false + } + }, + "afterSign": "build/notarize.js" } diff --git a/package-lock.json b/package-lock.json index 9a99590a..3f59677d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -150,6 +150,7 @@ "node_modules/@codemirror/autocomplete": { "version": "6.19.0", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -190,6 +191,7 @@ "node_modules/@codemirror/lang-css": { "version": "6.3.1", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", @@ -212,6 +214,7 @@ "node_modules/@codemirror/lang-html": { "version": "6.4.10", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-css": "^6.0.0", @@ -235,6 +238,7 @@ "node_modules/@codemirror/lang-javascript": { "version": "6.2.4", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.6.0", @@ -394,6 +398,7 @@ "node_modules/@codemirror/language": { "version": "6.11.3", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -459,6 +464,7 @@ "node_modules/@codemirror/state": { "version": "6.5.2", "license": "MIT", + "peer": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -476,6 +482,7 @@ "node_modules/@codemirror/view": { "version": "6.38.4", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -1445,7 +1452,6 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, - "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -1465,7 +1471,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -1483,7 +1488,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1498,7 +1502,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -1510,15 +1513,13 @@ "version": "2.1.3", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/@electron/windows-sign/node_modules/universalify": { "version": "2.0.1", "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 10.0.0" } @@ -2037,7 +2038,8 @@ }, "node_modules/@lezer/common": { "version": "1.2.3", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@lezer/cpp": { "version": "1.1.3", @@ -2069,6 +2071,7 @@ "node_modules/@lezer/highlight": { "version": "1.2.1", "license": "MIT", + "peer": true, "dependencies": { "@lezer/common": "^1.0.0" } @@ -2094,6 +2097,7 @@ "node_modules/@lezer/javascript": { "version": "1.5.4", "license": "MIT", + "peer": true, "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.1.3", @@ -2112,6 +2116,7 @@ "node_modules/@lezer/lr": { "version": "1.4.2", "license": "MIT", + "peer": true, "dependencies": { "@lezer/common": "^1.0.0" } @@ -3628,6 +3633,7 @@ "version": "7.6.13", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -3765,6 +3771,7 @@ "node_modules/@types/express": { "version": "5.0.3", "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -3859,6 +3866,7 @@ "node_modules/@types/node": { "version": "24.6.1", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.13.0" } @@ -3881,6 +3889,7 @@ "node_modules/@types/react": { "version": "19.1.17", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3889,6 +3898,7 @@ "version": "19.1.11", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.0.0" } @@ -3948,8 +3958,7 @@ }, "node_modules/@types/trusted-types": { "version": "1.0.6", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/unist": { "version": "3.0.3", @@ -4018,6 +4027,7 @@ "version": "8.45.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", @@ -4438,7 +4448,8 @@ }, "node_modules/@xterm/xterm": { "version": "5.5.0", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/7zip-bin": { "version": "5.2.0", @@ -4465,6 +4476,7 @@ "version": "8.15.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4515,6 +4527,7 @@ "version": "6.12.6", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4870,6 +4883,7 @@ "version": "12.4.1", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" @@ -5768,6 +5782,7 @@ "version": "9.0.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -5813,8 +5828,7 @@ "version": "0.1.0", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -6205,6 +6219,7 @@ "version": "26.0.12", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "app-builder-lib": "26.0.12", "builder-util": "26.0.11", @@ -6607,7 +6622,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -6626,7 +6640,6 @@ "version": "4.4.3", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -6643,7 +6656,6 @@ "version": "7.0.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -6656,8 +6668,7 @@ "node_modules/electron-winstaller/node_modules/ms": { "version": "2.1.3", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/electron/node_modules/@types/node": { "version": "22.18.8", @@ -6873,6 +6884,7 @@ "version": "9.36.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8264,6 +8276,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.27.6" }, @@ -11197,7 +11210,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -11213,7 +11225,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -11557,6 +11568,7 @@ "node_modules/react": { "version": "19.1.1", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -11564,6 +11576,7 @@ "node_modules/react-dom": { "version": "19.1.1", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -11586,6 +11599,7 @@ "node_modules/react-hook-form": { "version": "7.63.0", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -11630,7 +11644,8 @@ }, "node_modules/react-is": { "version": "16.13.1", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-markdown": { "version": "10.1.0", @@ -11718,6 +11733,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -11917,7 +11933,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -13059,7 +13076,6 @@ "version": "0.9.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -13113,7 +13129,6 @@ "version": "0.5.6", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -13125,7 +13140,6 @@ "version": "2.6.3", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -13210,6 +13224,7 @@ "node_modules/tinyglobby/node_modules/picomatch": { "version": "4.0.3", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -13375,6 +13390,7 @@ "version": "5.9.3", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13684,6 +13700,7 @@ "node_modules/vite": { "version": "7.1.7", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -13771,6 +13788,7 @@ "node_modules/vite/node_modules/picomatch": { "version": "4.0.3", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/package.json b/package.json index 7b8c2354..3e1580ee 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,10 @@ "build:linux-portable": "npm run build && electron-builder --linux --dir", "build:linux-appimage": "npm run build && electron-builder --linux AppImage", "build:linux-targz": "npm run build && electron-builder --linux tar.gz", + "build:mac-dmg": "npm run build && electron-builder --mac dmg", + "build:mac-zip": "npm run build && electron-builder --mac zip", + "build:mac-mas": "npm run build && electron-builder --mac mas", + "build:mac-universal": "npm run build && electron-builder --mac --universal", "test:encryption": "tsc -p tsconfig.node.json && node ./dist/backend/backend/utils/encryption-test.js", "migrate:encryption": "tsc -p tsconfig.node.json && node ./dist/backend/backend/utils/encryption-migration.js", "prepare": "husky" @@ -114,6 +118,7 @@ "devDependencies": { "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", + "@electron/notarize": "^2.5.0", "@eslint/js": "^9.34.0", "@types/better-sqlite3": "^7.6.13", "@types/cors": "^2.8.19",