技能 编程开发 苹果备忘录自动化CI测试指南

苹果备忘录自动化CI测试指南

v20260423
apple-notes-ci-integration
本指南专门解决在CI环境中运行macOS Apple备忘录自动化测试的权限问题。由于标准CI环境的TCC(透明度、同意与控制)限制,直接的自动化脚本执行往往会失败。本文档提供了使用模拟客户端进行单元测试的模式,并指导了在具备足够权限的自托管运行器上进行真实集成测试的最佳实践。
获取技能
420 次下载
概览

Apple Notes CI Integration

Overview

Apple Notes automation is macOS-only because it depends on the Apple Events subsystem and Notes.app. CI pipelines must use GitHub Actions macOS runners (macos-latest or macos-14). However, macOS CI runners have restricted TCC (Transparency, Consent, and Control) permissions, which means direct Notes.app automation via osascript will fail in CI. The standard pattern is to run unit tests against a mock JXA client in CI, and reserve real Notes.app integration tests for local macOS machines or self-hosted runners with pre-granted automation permissions.

GitHub Actions Workflow

# .github/workflows/notes-ci.yml
name: Notes Automation CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  unit-tests:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: "20", cache: "npm" }
      - run: npm ci
      - name: Verify macOS version
        run: sw_vers
      - name: Lint JXA scripts
        run: |
          # Validate JavaScript syntax in all .jxa files
          for f in scripts/*.jxa; do
            node --check "$f" 2>/dev/null || echo "WARN: $f is osascript-only"
          done
      - name: Unit tests (mocked Notes client)
        run: npm test
      - name: Validate JXA templates
        run: |
          # Ensure osascript can parse (but not execute) JXA scripts
          for f in scripts/*.jxa; do
            osascript -l JavaScript -e "$(cat "$f")" 2>&1 | grep -v "Not authorized" || true
          done

Mock Client for CI

// tests/mocks/notes-client.mock.ts
export class MockAppleNotesClient {
  private notes: Array<{ id: string; title: string; body: string; folder: string }> = [];

  createNote(title: string, body: string, folder = "Notes"): string {
    const id = `mock-note-${Date.now()}-${Math.random().toString(36).slice(2)}`;
    this.notes.push({ id, title, body, folder });
    return id;
  }

  listNotes() { return [...this.notes]; }
  getNote(id: string) { return this.notes.find(n => n.id === id) || null; }
  searchNotes(q: string) { return this.notes.filter(n => n.title.includes(q) || n.body.includes(q)); }
  deleteNote(id: string) { this.notes = this.notes.filter(n => n.id !== id); }
  getFolders() { return [...new Set(this.notes.map(n => n.folder))]; }
}

Self-Hosted Runner with TCC Pre-Approval

# On a self-hosted macOS runner, pre-grant automation permissions:
# 1. Open System Settings > Privacy & Security > Automation
# 2. Grant your CI user's terminal access to Notes.app
# 3. Verify with:
osascript -l JavaScript -e 'Application("Notes").defaultAccount.notes.length'

# For headless runners, use tccutil (requires SIP adjustment or MDM profile):
# sudo tccutil --insert com.apple.Notes --service AppleEvents --app /usr/bin/osascript

Error Handling

Issue Cause Solution
"Not authorized to send Apple events" in CI TCC blocks automation on CI runners Use mock client; real tests on self-hosted runner
osascript syntax errors not caught JXA has no standalone linter Use node --check for JS syntax; parse-only validation
Flaky tests on macos-latest Runner image updates change Notes state Pin to macos-14; always use mocked client
Tests pass locally, fail in CI Different macOS version or missing app Check sw_vers output; ensure Notes.app exists on runner
Timeout waiting for Notes.app App launch delay on cold runner Add open -a Notes && sleep 3 before osascript calls

Resources

Next Steps

For diagnosing CI failures, see apple-notes-common-errors. For production deployment of automation scripts, see apple-notes-deploy-integration.

信息
Category 编程开发
Name apple-notes-ci-integration
版本 v20260423
大小 4.5KB
更新时间 2026-07-13
语言