Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions Docs/RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Cue 업데이트 배포

Cue의 자동 업데이트는 두 서명을 함께 사용합니다.

- `Cue Local Signing`: 모든 배포 앱의 코드 서명을 동일하게 유지합니다.
- Sparkle 계정 `cue`: 내려받는 업데이트 파일이 Cue에서 만든 파일인지
EdDSA로 확인합니다.

둘 중 하나를 잃어버리거나 새로 만들면 기존 설치본이 다음 업데이트를
받지 못할 수 있습니다. 로그인 키체인과 Sparkle 개인 키는 별도의 안전한
저장소에 백업해 주세요. 개인 키 파일은 저장소에 올리지 않습니다.

```sh
.build/artifacts/sparkle/Sparkle/bin/generate_keys \
--account cue \
-x /안전한/외부/경로/cue-sparkle.key
```

## 새 버전 배포

1. `Info.plist`의 표시 버전과 빌드 번호를 올립니다.
2. 업데이트 앱과 appcast를 만듭니다.

```sh
./Scripts/build-update.sh
```

3. 코드 변경과 새 `appcast.xml`을 `main`에 반영합니다.
4. 같은 버전의 GitHub Release를 만들고 생성된 ZIP을 올립니다.

```sh
CUE_RELEASE_VERSION=0.8.7
gh release create "v$CUE_RELEASE_VERSION" \
"dist/Cue-$CUE_RELEASE_VERSION-macos.zip" \
--title "Cue $CUE_RELEASE_VERSION" \
--notes-file /path/to/release-notes.md
```

`appcast.xml`의 다운로드 주소와 GitHub Release의 태그·파일명이 정확히
같아야 합니다. `build-update.sh`는 앱 코드 서명, ZIP 무결성, EdDSA 서명을
확인한 뒤 파일을 만듭니다.

## 배포 후 확인

```sh
CUE_RELEASE_VERSION=0.8.7
curl -fsSL \
https://raw.githubusercontent.com/exemchs/cue/main/appcast.xml

curl -I -L \
"https://github.com/exemchs/cue/releases/download/v$CUE_RELEASE_VERSION/Cue-$CUE_RELEASE_VERSION-macos.zip"
```

릴리스 ZIP을 다시 내려받아 압축을 풀고 코드 서명도 확인합니다.

```sh
codesign --verify --deep --strict /path/to/Cue.app
```
43 changes: 15 additions & 28 deletions Docs/TEAM_DISTRIBUTION.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
# 팀원에게 Cue 공유하기

Cue는 GitHub 저장소를 공유하고, 각자의 Mac에서 직접 빌드해 설치합니다.

한 Mac에서 로컬 서명한 `Cue.app`을 그대로 다른 사람에게 보내면 macOS가
다른 앱으로 인식하거나 권한을 다시 요청할 수 있습니다. 완성된 앱 파일보다
저장소 주소와 설치 명령을 전달하는 편이 안정적입니다.
Cue는 GitHub Release에 올린 동일한 앱을 팀원이 받아 사용합니다. 이후
버전도 같은 서명으로 배포하기 때문에 전체 디스크 접근 권한을 매번 다시
허용하지 않아도 됩니다.

## 처음 설치할 때

```sh
git clone https://github.com/exemchs/cue.git
cd cue
./Scripts/setup-signing-identity.sh
./Scripts/allow-local-signing.sh
./Scripts/build-app.sh
ditto dist/Cue.app /Applications/Cue.app
open /Applications/Cue.app
```

`allow-local-signing.sh`는 로그인 키체인 암호를 직접 입력받기 때문에
Terminal에서 실행해야 합니다. 입력한 암호는 저장하지 않습니다.
1. [최신 릴리스](https://github.com/exemchs/cue/releases/latest)에서
`Cue-버전-macos.zip`을 받습니다.
2. 압축을 풀고 `Cue.app`을 `/Applications`로 옮깁니다.
3. 처음 한 번은 `Cue.app`을 Control-클릭하고 **열기**를 선택합니다.

앱을 실행한 뒤에는 다음 항목만 설정합니다.

Expand All @@ -33,19 +23,16 @@ Terminal에서 실행해야 합니다. 입력한 암호는 저장하지 않습
Mail 같은 소스를 추가할 때 앱마다 별도 권한을 허용할 필요는 없습니다.
아이콘도 설치된 앱에서 자동으로 가져옵니다.

## 업데이트할 때

기존 저장소에서 최신 코드를 받은 뒤 같은 명령으로 다시 빌드합니다.
## 자동 업데이트

```sh
git pull
./Scripts/build-app.sh
ditto dist/Cue.app /Applications/Cue.app
open /Applications/Cue.app
```
Cue는 6시간마다 GitHub에서 새 버전을 확인합니다. 업데이트 파일은 EdDSA
서명을 통과한 경우에만 사용하고, Cue를 다음에 종료하거나 다시 실행할 때
자동으로 설치합니다. Cue를 하루 이상 계속 켜두면 설치 준비가 끝났다는
안내가 나타날 수 있습니다.

권한 요청이 반복되지 않도록 처음 만든 로컬 서명 인증서와
`/Applications/Cue.app` 경로를 계속 사용합니다.
0.8.6 이하에는 업데이터가 없으므로 최신 버전을 한 번 직접 설치해야 합니다.
기존에 각자 빌드한 Cue를 사용했다면 공유 빌드로 바뀌는 첫 설치에서만 전체
디스크 접근 권한을 다시 켜야 할 수 있습니다.

## 회사에서 관리하는 Mac

Expand Down
22 changes: 20 additions & 2 deletions Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,32 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.8.6</string>
<string>0.8.7</string>
<key>CFBundleVersion</key>
<string>42</string>
<string>43</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
<key>LSUIElement</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
<key>SUAllowsAutomaticUpdates</key>
<true/>
<key>SUAutomaticallyUpdate</key>
<true/>
<key>SUEnableAutomaticChecks</key>
<true/>
<key>SUFeedURL</key>
<string>https://raw.githubusercontent.com/exemchs/cue/main/appcast.xml</string>
<key>SUPublicEDKey</key>
<string>/HhZoRAFWV8E0I80JwexpiZBvqE31OeMGCXwM4BHNmw=</string>
<key>SUScheduledCheckInterval</key>
<integer>21600</integer>
<key>SUScheduledImpatientCheckInterval</key>
<integer>86400</integer>
<key>SUShowReleaseNotes</key>
<false/>
<key>SUVerifyUpdateBeforeExtraction</key>
<true/>
</dict>
</plist>
12 changes: 9 additions & 3 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@ Mattermost 연결 파일에는 알림을 구분하기 위한 SHA-256 값, 확인

## 네트워크 연결

Cue는 OpenAI, Mattermost 또는 Cue 서버에 접속하지 않습니다. Mattermost
연결은 현재 Mac의 `127.0.0.1:49371` 안에서만 동작합니다. Mattermost 앱
자체는 원래 사용하던 네트워크 연결을 그대로 사용합니다.
Cue는 OpenAI, Mattermost 또는 별도 Cue 서버에 작업과 알림 데이터를
보내지 않습니다. Mattermost 연결은 현재 Mac의 `127.0.0.1:49371`
안에서만 동작합니다. Mattermost 앱 자체는 원래 사용하던 네트워크 연결을
그대로 사용합니다.

자동 업데이트를 확인하고 받을 때만 `raw.githubusercontent.com`과
`github.com`에 연결합니다. 이 요청에는 Cue 버전과 macOS가 일반적인
네트워크 요청에 포함하는 정보만 전달되며, 작업 제목, 메시지, 소스 설정은
포함하지 않습니다. 내려받은 앱은 EdDSA 서명을 확인한 뒤 설치합니다.

## 로컬 데이터 지우기

Expand Down
15 changes: 15 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,29 @@ let package = Package(
products: [
.executable(name: "Cue", targets: ["Cue"])
],
dependencies: [
.package(
url: "https://github.com/sparkle-project/Sparkle",
exact: "2.9.2"
)
],
targets: [
.executableTarget(
name: "Cue",
dependencies: [
.product(name: "Sparkle", package: "Sparkle")
],
resources: [
.process("Resources")
],
linkerSettings: [
.linkedLibrary("sqlite3")
.linkedLibrary("sqlite3"),
.unsafeFlags([
"-Xlinker",
"-rpath",
"-Xlinker",
"@executable_path/../Frameworks"
])
]
)
]
Expand Down
54 changes: 23 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ Cue는 진행 중인 작업과 확인이 필요한 알림을<br>
</div>

> [!IMPORTANT]
> Cue는 아직 직접 빌드해 사용하는 초기 버전입니다. 일부 기능은 공식 API
> 대신 각 앱이 Mac에 저장한 로컬 데이터를 읽습니다. macOS나 앱이
> 업데이트되면 일부 연결이 동작하지 않을 수 있습니다. 권한을 허용하기 전
> Cue의 일부 기능은 공식 API 대신 각 앱이 Mac에 저장한 로컬 데이터를
> 읽습니다. macOS나 앱이 업데이트되면 일부 연결이 동작하지 않을 수
> 있습니다. 권한을 허용하기 전
> [개인정보 처리 방식](PRIVACY.md)을 확인해 주세요.

## 왜 만들었나요
Expand Down Expand Up @@ -89,39 +89,18 @@ macOS 알림 센터에 저장된 알림 제목과 내용을 보여줍니다. 앱

## 설치

현재는 각자의 Mac에서 직접 빌드해 설치합니다. Pretendard Light, Regular,
Medium은 Cue에 포함되어 있어 따로 설치하지 않아도 됩니다.
1. [최신 릴리스](https://github.com/exemchs/cue/releases/latest)에서
`Cue-버전-macos.zip`을 받습니다.
2. 압축을 풀고 `Cue.app`을 `/Applications`로 옮깁니다.
3. 처음 한 번은 `Cue.app`을 Control-클릭하고 **열기**를 선택합니다.

### 필요한 환경

- 노치가 있는 MacBook
- macOS 14 이상
- Swift 6을 포함한 Xcode Command Line Tools
- 상태나 알림을 확인할 ChatGPT, Mattermost 또는 다른 앱
- Homebrew Node.js (Mattermost 알림을 원래 대화로 연결할 때만 필요)

Command Line Tools가 없다면 먼저 설치합니다.

```sh
xcode-select --install
```

Cue를 내려받아 빌드하고 `/Applications`에 설치합니다.

```sh
git clone https://github.com/exemchs/cue.git
cd cue
./Scripts/setup-signing-identity.sh
./Scripts/allow-local-signing.sh
./Scripts/build-app.sh
ditto dist/Cue.app /Applications/Cue.app
open /Applications/Cue.app
```

`allow-local-signing.sh`는 로그인 키체인 암호를 직접 입력받기 때문에
Terminal에서 실행해야 합니다. 입력한 암호는 화면에 표시되거나 저장되지
않습니다. 만들어진 로컬 인증서도 각 Mac에만 남습니다.

설치 후에는 다음 항목만 연결하면 됩니다.

1. 노치의 Cue 영역을 오른쪽 클릭한 뒤 **앱 추가**를 누릅니다.
Expand All @@ -130,8 +109,10 @@ Terminal에서 실행해야 합니다. 입력한 암호는 화면에 표시되
보안 → 전체 디스크 접근 권한**에서 `/Applications/Cue.app`을 켭니다.
4. macOS가 요청하는 경우 Cue를 한 번 다시 실행합니다.

권한을 반복해서 요청하지 않도록 Cue는 `/Applications`에 두고, 업데이트할
때도 같은 로컬 서명 인증서를 사용합니다.
Cue는 6시간마다 새 버전을 확인하고, 서명을 확인한 업데이트만 백그라운드로
받습니다. 받은 업데이트는 Cue를 다음에 종료하거나 다시 실행할 때
설치됩니다. 0.8.6 이하를 사용 중이라면 위 설치 과정을 한 번만 다시
진행해야 하며, 그다음부터 자동으로 업데이트됩니다.

## 데이터는 어떻게 처리하나요

Expand All @@ -140,6 +121,7 @@ Terminal에서 실행해야 합니다. 입력한 암호는 화면에 표시되
- 분석 도구를 사용하지 않습니다.
- 작업과 알림 데이터를 외부 서버로 보내지 않습니다.
- 설정과 확인 기록은 현재 Mac에만 저장합니다.
- 새 버전 확인을 위해 GitHub에만 연결합니다.

Cue가 어떤 파일을 읽고 무엇을 저장하는지는
[PRIVACY.md](PRIVACY.md)에 정리해두었습니다.
Expand All @@ -165,17 +147,26 @@ swift run Cue --preview-regular-app --preview-many-platforms \
./Scripts/build-app.sh
```

업데이트 파일과 appcast 만들기(배포 관리자):

```sh
./Scripts/build-update.sh
```

주요 구성:

- `CueModel`: 소스 설정, 상태 정리, 로컬 저장
- `LocalActivityMonitor`: Codex와 macOS 알림 센터 읽기
- `NotificationDatabaseWatcher`: 새 macOS 알림을 감지해 목록 갱신
- `MattermostBridgeSupervisor`: Mattermost의 원래 대화로 이동하기 위한 로컬 연결
- `Sparkle`: GitHub 릴리스 확인, 업데이트 검증과 설치
- `CueNotchView`: 노치, 활동 목록, 소스 관리, 순서 변경
- `NotchPanelController`: 노치 위치와 멀티 디스플레이 동작

팀원에게 공유하는 방법은
[팀원 공유 문서](Docs/TEAM_DISTRIBUTION.md)에 정리해두었습니다.
업데이트를 배포하는 순서는
[배포 문서](Docs/RELEASING.md)에서 확인할 수 있습니다.

## 함께 만들기

Expand All @@ -191,7 +182,8 @@ Cue는 [MIT License](LICENSE)로 배포합니다.
앱에 포함된 Pretendard 1.3.9는 SIL Open Font License 1.1을 따릅니다.
라이선스 원문은
`Sources/Cue/Resources/Licenses/Pretendard-OFL-1.1.txt`에 포함되어
있습니다.
있습니다. 자동 업데이트에 사용하는 Sparkle은 MIT License를 따르며,
배포 앱 안에 라이선스 원문을 함께 포함합니다.

ChatGPT, OpenAI, Mattermost, macOS와 각 앱의 이름 및 아이콘은 해당
권리자에게 속합니다. Cue는 해당 회사의 공식 제품이 아닙니다.
10 changes: 9 additions & 1 deletion Scripts/build-app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,24 @@ swift build -c release

STAGING_DIR="$(mktemp -d "$DIST_DIR/.cue-package.XXXXXX")"
STAGED_APP="$STAGING_DIR/Cue.app"
mkdir -p "$STAGED_APP/Contents/MacOS" "$STAGED_APP/Contents/Resources"
mkdir -p \
"$STAGED_APP/Contents/MacOS" \
"$STAGED_APP/Contents/Resources" \
"$STAGED_APP/Contents/Frameworks"
cp "$BUILD_DIR/release/Cue" "$STAGED_APP/Contents/MacOS/Cue"

ditto "$BUILD_DIR/release/Sparkle.framework" \
"$STAGED_APP/Contents/Frameworks/Sparkle.framework"

cp "$PROJECT_DIR/Sources/Cue/Resources/"*.mjs \
"$STAGED_APP/Contents/Resources/"

ditto "$PROJECT_DIR/Sources/Cue/Resources/Fonts" \
"$STAGED_APP/Contents/Resources/Fonts"
ditto "$PROJECT_DIR/Sources/Cue/Resources/Licenses" \
"$STAGED_APP/Contents/Resources/Licenses"
cp "$BUILD_DIR/checkouts/Sparkle/LICENSE" \
"$STAGED_APP/Contents/Resources/Licenses/Sparkle-LICENSE"

cp "$PROJECT_DIR/Info.plist" "$STAGED_APP/Contents/Info.plist"
plutil -replace CFBundleIdentifier \
Expand Down
Loading