mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
Set up Signing and Notarizing for MacOS
This commit is contained in:
parent
2380d0af85
commit
226cf8dfd7
194
.github/actions/build-electron/action.yml
vendored
194
.github/actions/build-electron/action.yml
vendored
@ -1,3 +1,6 @@
|
|||||||
|
name: "Build Electron App"
|
||||||
|
description: "Builds and packages the Electron app for different platforms"
|
||||||
|
|
||||||
inputs:
|
inputs:
|
||||||
os:
|
os:
|
||||||
description: "One of the supported platforms: macos, linux, windows"
|
description: "One of the supported platforms: macos, linux, windows"
|
||||||
@ -8,13 +11,45 @@ inputs:
|
|||||||
extension:
|
extension:
|
||||||
description: "Platform specific extensions to copy in the output: dmg, deb, rpm, exe, zip"
|
description: "Platform specific extensions to copy in the output: dmg, deb, rpm, exe, zip"
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Python for appdmg to be installed
|
# Certificate setup
|
||||||
|
- name: Import Apple certificates
|
||||||
|
if: inputs.os == 'macos'
|
||||||
|
uses: apple-actions/import-codesign-certs@v2
|
||||||
|
with:
|
||||||
|
p12-file-base64: ${{ env.APPLE_APP_CERTIFICATE_BASE64 }}
|
||||||
|
p12-password: ${{ env.APPLE_APP_CERTIFICATE_PASSWORD }}
|
||||||
|
keychain: build
|
||||||
|
keychain-password: ${{ github.run_id }}
|
||||||
|
|
||||||
|
- name: Install Installer certificate
|
||||||
|
if: inputs.os == 'macos'
|
||||||
|
uses: apple-actions/import-codesign-certs@v2
|
||||||
|
with:
|
||||||
|
p12-file-base64: ${{ env.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
|
||||||
|
p12-password: ${{ env.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
|
||||||
|
keychain: build
|
||||||
|
keychain-password: ${{ github.run_id }}
|
||||||
|
# We don't need to create a keychain here because we're using the build keychain that was created in the previous step
|
||||||
|
create-keychain: false
|
||||||
|
|
||||||
|
- name: Verify certificates
|
||||||
|
if: inputs.os == 'macos'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "Available signing identities:"
|
||||||
|
security find-identity -v -p codesigning build.keychain
|
||||||
|
|
||||||
|
- name: Set up Python and other macOS dependencies
|
||||||
if: ${{ inputs.os == 'macos' }}
|
if: ${{ inputs.os == 'macos' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: brew install python-setuptools
|
run: |
|
||||||
|
brew install python-setuptools
|
||||||
|
brew install create-dmg
|
||||||
|
|
||||||
- name: Install dependencies for RPM and Flatpak package building
|
- name: Install dependencies for RPM and Flatpak package building
|
||||||
if: ${{ inputs.os == 'linux' }}
|
if: ${{ inputs.os == 'linux' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -24,21 +59,160 @@ runs:
|
|||||||
FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi)
|
FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi)
|
||||||
FLATPAK_VERSION='24.08'
|
FLATPAK_VERSION='24.08'
|
||||||
flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION
|
flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION
|
||||||
|
|
||||||
|
# Build setup
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Update build info
|
- name: Update build info
|
||||||
shell: bash
|
shell: bash
|
||||||
run: npm run chore:update-build-info
|
run: npm run chore:update-build-info
|
||||||
- name: Run electron-forge
|
|
||||||
|
# Critical debugging configuration
|
||||||
|
- name: Run electron-forge build with enhanced logging
|
||||||
shell: bash
|
shell: bash
|
||||||
run: npm run electron-forge:make -- --arch=${{ inputs.arch }}
|
env:
|
||||||
|
DEBUG: "electron-osx-sign*,@electron/notarize*,electron-forge:*"
|
||||||
|
ELECTRON_NOTARIZE_DEBUG: 1
|
||||||
|
ELECTRON_ENABLE_LOGGING: 1
|
||||||
|
ELECTRON_DEBUG_NOTARIZATION: 1
|
||||||
|
# Pass through required environment variables for signing and notarization
|
||||||
|
APPLE_TEAM_ID: ${{ env.APPLE_TEAM_ID }}
|
||||||
|
APPLE_ID: ${{ env.APPLE_ID }}
|
||||||
|
APPLE_ID_PASSWORD: ${{ env.APPLE_ID_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
# Map OS names to Electron Forge platform names
|
||||||
|
if [ "${{ inputs.os }}" = "macos" ]; then
|
||||||
|
PLATFORM="darwin"
|
||||||
|
elif [ "${{ inputs.os }}" = "windows" ]; then
|
||||||
|
PLATFORM="win32"
|
||||||
|
else
|
||||||
|
PLATFORM="${{ inputs.os }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm run electron-forge:make -- \
|
||||||
|
--arch=${{ inputs.arch }} \
|
||||||
|
--platform=$PLATFORM \
|
||||||
|
--verbose
|
||||||
|
|
||||||
|
# Add DMG signing step
|
||||||
|
- name: Sign DMG
|
||||||
|
if: inputs.os == 'macos'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "Signing DMG file..."
|
||||||
|
dmg_file=$(find out -name "*.dmg" -print -quit)
|
||||||
|
if [ -n "$dmg_file" ]; then
|
||||||
|
echo "Found DMG: $dmg_file"
|
||||||
|
# Get the first valid signing identity from the keychain
|
||||||
|
SIGNING_IDENTITY=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application" | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
|
||||||
|
if [ -z "$SIGNING_IDENTITY" ]; then
|
||||||
|
echo "Error: No valid Developer ID Application certificate found in keychain"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Using signing identity: $SIGNING_IDENTITY"
|
||||||
|
# Sign the DMG
|
||||||
|
codesign --force --sign "$SIGNING_IDENTITY" --options runtime --timestamp "$dmg_file"
|
||||||
|
# Notarize the DMG
|
||||||
|
xcrun notarytool submit "$dmg_file" --apple-id "$APPLE_ID" --password "$APPLE_ID_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait
|
||||||
|
# Staple the notarization ticket
|
||||||
|
xcrun stapler staple "$dmg_file"
|
||||||
|
else
|
||||||
|
echo "No DMG found to sign"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Verify code signing
|
||||||
|
if: inputs.os == 'macos'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "Verifying code signing for all artifacts..."
|
||||||
|
|
||||||
|
# First check the .app bundle
|
||||||
|
echo "Looking for .app bundle..."
|
||||||
|
app_bundle=$(find out -name "*.app" -print -quit)
|
||||||
|
if [ -n "$app_bundle" ]; then
|
||||||
|
echo "Found app bundle: $app_bundle"
|
||||||
|
echo "Verifying app bundle signing..."
|
||||||
|
codesign --verify --deep --strict --verbose=2 "$app_bundle"
|
||||||
|
echo "Displaying app bundle signing info..."
|
||||||
|
codesign --display --verbose=2 "$app_bundle"
|
||||||
|
|
||||||
|
echo "Checking entitlements..."
|
||||||
|
codesign --display --entitlements :- "$app_bundle"
|
||||||
|
|
||||||
|
echo "Checking notarization status..."
|
||||||
|
xcrun stapler validate "$app_bundle" || echo "Warning: App bundle not notarized yet"
|
||||||
|
else
|
||||||
|
echo "No .app bundle found to verify"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Then check DMG if it exists
|
||||||
|
echo "Looking for DMG..."
|
||||||
|
dmg_file=$(find out -name "*.dmg" -print -quit)
|
||||||
|
if [ -n "$dmg_file" ]; then
|
||||||
|
echo "Found DMG: $dmg_file"
|
||||||
|
echo "Verifying DMG signing..."
|
||||||
|
codesign --verify --deep --strict --verbose=2 "$dmg_file"
|
||||||
|
echo "Displaying DMG signing info..."
|
||||||
|
codesign --display --verbose=2 "$dmg_file"
|
||||||
|
|
||||||
|
echo "Checking DMG notarization..."
|
||||||
|
xcrun stapler validate "$dmg_file" || echo "Warning: DMG not notarized yet"
|
||||||
|
else
|
||||||
|
echo "No DMG found to verify"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Finally check ZIP if it exists
|
||||||
|
echo "Looking for ZIP..."
|
||||||
|
zip_file=$(find out -name "*.zip" -print -quit)
|
||||||
|
if [ -n "$zip_file" ]; then
|
||||||
|
echo "Found ZIP: $zip_file"
|
||||||
|
echo "Note: ZIP files are not code signed, but their contents should be"
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Prepare artifacts
|
- name: Prepare artifacts
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir -p upload;
|
mkdir -p upload
|
||||||
for ext in ${{ inputs.extension }};
|
|
||||||
do
|
if [ "${{ inputs.os }}" = "macos" ]; then
|
||||||
file=$(find out/make -name "*.$ext" -print -quit);
|
# For macOS, we need to look in specific directories based on the maker
|
||||||
cp "$file" "upload/TriliumNextNotes-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }}.$ext";
|
echo "Collecting macOS artifacts..."
|
||||||
done
|
|
||||||
|
# Look for DMG files recursively
|
||||||
|
echo "Looking for DMG files..."
|
||||||
|
dmg_file=$(find out -name "*.dmg" -print -quit)
|
||||||
|
if [ -n "$dmg_file" ]; then
|
||||||
|
echo "Found DMG: $dmg_file"
|
||||||
|
cp "$dmg_file" "upload/TriliumNextNotes-${{ github.ref_name }}-darwin-${{ inputs.arch }}.dmg"
|
||||||
|
else
|
||||||
|
echo "Warning: No DMG file found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Look for ZIP files recursively
|
||||||
|
echo "Looking for ZIP files..."
|
||||||
|
zip_file=$(find out -name "*.zip" -print -quit)
|
||||||
|
if [ -n "$zip_file" ]; then
|
||||||
|
echo "Found ZIP: $zip_file"
|
||||||
|
cp "$zip_file" "upload/TriliumNextNotes-${{ github.ref_name }}-darwin-${{ inputs.arch }}.zip"
|
||||||
|
else
|
||||||
|
echo "Warning: No ZIP file found"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# For other platforms, use the existing logic but with better error handling
|
||||||
|
echo "Collecting artifacts for ${{ inputs.os }}..."
|
||||||
|
for ext in ${{ inputs.extension }}; do
|
||||||
|
echo "Looking for .$ext files..."
|
||||||
|
file=$(find out -name "*.$ext" -print -quit)
|
||||||
|
if [ -n "$file" ]; then
|
||||||
|
echo "Found $file for extension $ext"
|
||||||
|
cp "$file" "upload/TriliumNextNotes-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }}.$ext"
|
||||||
|
else
|
||||||
|
echo "Warning: No file found with extension .$ext"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Final contents of upload directory:"
|
||||||
|
ls -la upload/
|
||||||
|
41
.github/workflows/main.yml
vendored
41
.github/workflows/main.yml
vendored
@ -33,6 +33,36 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os.image }}
|
runs-on: ${{ matrix.os.image }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Set up certificates and keychain for macOS
|
||||||
|
- name: Install Apple Certificates
|
||||||
|
if: matrix.os.name == 'macos'
|
||||||
|
env:
|
||||||
|
APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }}
|
||||||
|
APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }}
|
||||||
|
INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
|
||||||
|
INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
|
||||||
|
KEYCHAIN_PASSWORD: ${{ github.run_id }}
|
||||||
|
run: |
|
||||||
|
# Create keychain
|
||||||
|
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
||||||
|
security default-keychain -s build.keychain
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
|
||||||
|
security set-keychain-settings -t 3600 -u build.keychain
|
||||||
|
|
||||||
|
# Import application certificate
|
||||||
|
echo "$APP_CERTIFICATE_BASE64" | base64 --decode > application.p12
|
||||||
|
security import application.p12 -k build.keychain -P "$APP_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
|
||||||
|
rm application.p12
|
||||||
|
|
||||||
|
# Import installer certificate
|
||||||
|
echo "$INSTALLER_CERTIFICATE_BASE64" | base64 --decode > installer.p12
|
||||||
|
security import installer.p12 -k build.keychain -P "$INSTALLER_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
|
||||||
|
rm installer.p12
|
||||||
|
|
||||||
|
# Update keychain settings
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
|
||||||
|
|
||||||
- name: Set up node & dependencies
|
- name: Set up node & dependencies
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
@ -43,6 +73,17 @@ jobs:
|
|||||||
os: ${{ matrix.os.name }}
|
os: ${{ matrix.os.name }}
|
||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
extension: ${{ matrix.os.extension }}
|
extension: ${{ matrix.os.extension }}
|
||||||
|
env:
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||||
|
|
||||||
|
# Clean up keychain after build
|
||||||
|
- name: Clean up keychain
|
||||||
|
if: matrix.os.name == 'macos' && always()
|
||||||
|
run: |
|
||||||
|
security delete-keychain build.keychain
|
||||||
|
|
||||||
- name: Publish artifacts
|
- name: Publish artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
@ -40,6 +40,15 @@ jobs:
|
|||||||
os: ${{ matrix.os.name }}
|
os: ${{ matrix.os.name }}
|
||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
extension: ${{ join(matrix.os.extension, ' ') }}
|
extension: ${{ join(matrix.os.extension, ' ') }}
|
||||||
|
env:
|
||||||
|
APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }}
|
||||||
|
APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }}
|
||||||
|
APPLE_INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
|
||||||
|
APPLE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||||
|
|
||||||
- name: Publish release
|
- name: Publish release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
12
entitlements.plist
Normal file
12
entitlements.plist
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?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>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -1,7 +1,7 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
|
|
||||||
const APP_NAME = "TriliumNext Notes";
|
const APP_NAME = "TriliumNextNotes";
|
||||||
|
|
||||||
const extraResourcesForPlatform = getExtraResourcesForPlatform();
|
const extraResourcesForPlatform = getExtraResourcesForPlatform();
|
||||||
const baseLinuxMakerConfigOptions = {
|
const baseLinuxMakerConfigOptions = {
|
||||||
@ -17,33 +17,37 @@ module.exports = {
|
|||||||
overwrite: true,
|
overwrite: true,
|
||||||
asar: true,
|
asar: true,
|
||||||
icon: "./images/app-icons/icon",
|
icon: "./images/app-icons/icon",
|
||||||
|
osxSign: {},
|
||||||
|
osxNotarize: {
|
||||||
|
appleId: process.env.APPLE_ID,
|
||||||
|
appleIdPassword: process.env.APPLE_ID_PASSWORD,
|
||||||
|
teamId: process.env.APPLE_TEAM_ID
|
||||||
|
},
|
||||||
extraResource: [
|
extraResource: [
|
||||||
// Moved to root
|
// All resources should stay in Resources directory for macOS
|
||||||
...extraResourcesForPlatform,
|
...(process.platform === "darwin" ? [] : extraResourcesForPlatform),
|
||||||
|
|
||||||
// Moved to resources (TriliumNext Notes.app/Contents/Resources on macOS)
|
// These always go in Resources
|
||||||
"translations/",
|
"translations/",
|
||||||
"node_modules/@highlightjs/cdn-assets/styles"
|
"node_modules/@highlightjs/cdn-assets/styles"
|
||||||
],
|
],
|
||||||
afterComplete: [
|
afterComplete: [
|
||||||
(buildPath, _electronVersion, platform, _arch, callback) => {
|
(buildPath, _electronVersion, platform, _arch, callback) => {
|
||||||
for (const resource of extraResourcesForPlatform) {
|
// Only move resources on non-macOS platforms
|
||||||
const baseName = path.basename(resource);
|
if (platform !== "darwin") {
|
||||||
|
for (const resource of extraResourcesForPlatform) {
|
||||||
|
const baseName = path.basename(resource);
|
||||||
|
const sourcePath = path.join(buildPath, "resources", baseName);
|
||||||
|
const destPath = (baseName !== "256x256.png")
|
||||||
|
? path.join(buildPath, baseName)
|
||||||
|
: path.join(buildPath, "icon.png");
|
||||||
|
|
||||||
// prettier-ignore
|
fs.move(sourcePath, destPath)
|
||||||
const sourcePath = (platform === "darwin")
|
.then(() => callback())
|
||||||
? path.join(buildPath, `${APP_NAME}.app`, "Contents", "Resources", baseName)
|
.catch((err) => callback(err));
|
||||||
: path.join(buildPath, "resources", baseName);
|
}
|
||||||
|
} else {
|
||||||
// prettier-ignore
|
callback();
|
||||||
const destPath = (baseName !== "256x256.png")
|
|
||||||
? path.join(buildPath, baseName)
|
|
||||||
: path.join(buildPath, "icon.png");
|
|
||||||
|
|
||||||
// Copy files from resources folder to root
|
|
||||||
fs.move(sourcePath, destPath)
|
|
||||||
.then(() => callback())
|
|
||||||
.catch((err) => callback(err));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user