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",