Skip to content

Commit 017d85d

Browse files
committed
feat(release): prepare v1.0.0 release with Sparkle updates and security hardening
1 parent c20537a commit 017d85d

81 files changed

Lines changed: 2527 additions & 839 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/hooks.json

Lines changed: 0 additions & 32 deletions
This file was deleted.

.agents/hooks/post_file_change.sh

Lines changed: 0 additions & 13 deletions
This file was deleted.

.agents/hooks/safety_guard.sh

100755100644
File mode changed.

.github/workflows/ci.yml

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: CI
1+
name: Build & Tests
22

33
on:
44
push:
@@ -8,24 +8,28 @@ on:
88
workflow_dispatch:
99

1010
jobs:
11-
test-and-build:
12-
name: Build & Unit Test
13-
runs-on: macos-26
11+
build-and-test:
12+
name: Build & Tests
13+
runs-on: macos-14
1414

1515
steps:
1616
- name: Checkout repository
1717
uses: actions/checkout@v4
18-
with:
19-
submodules: recursive
2018

21-
- name: Install XcodeGen
22-
run: brew install xcodegen
19+
- name: Select Xcode 16
20+
run: sudo xcode-select -s /Applications/Xcode_16.app || sudo xcode-select -s /Applications/Xcode_16.2.app || sudo xcode-select -s /Applications/Xcode_16.1.app
21+
22+
- name: Install Tools (XcodeGen & xcbeautify)
23+
run: brew install xcodegen xcbeautify
2324

2425
- name: Generate Xcode Project
2526
run: xcodegen generate
2627

27-
- name: Run Unit Tests
28-
run: ./scripts/test.sh --unit
28+
- name: Resolve Package Dependencies
29+
run: xcodebuild -resolvePackageDependencies -project OpenClip.xcodeproj
30+
31+
- name: Run Full Test Suite (0 skips)
32+
run: ./scripts/test.sh
2933

3034
- name: Build App (Release)
31-
run: xcodebuild -project OpenClip.xcodeproj -scheme OpenClip -configuration Release -destination 'generic/platform=macOS' ARCHS='arm64 x86_64' ONLY_ACTIVE_ARCH=NO build
35+
run: ./scripts/package_app.sh

.github/workflows/release.yml

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,62 @@ permissions:
1212
jobs:
1313
package-and-release:
1414
name: Package App & Create Release
15-
runs-on: macos-26
15+
runs-on: macos-14
1616

1717
steps:
1818
- name: Checkout repository
1919
uses: actions/checkout@v4
20-
with:
21-
submodules: recursive
20+
21+
- name: Select Xcode 16
22+
run: sudo xcode-select -s /Applications/Xcode_16.app || sudo xcode-select -s /Applications/Xcode_16.2.app || sudo xcode-select -s /Applications/Xcode_16.1.app
2223

2324
- name: Install XcodeGen
2425
run: brew install xcodegen
2526

26-
- name: Package App (.zip and .dmg)
27-
run: ./scripts/package_app.sh
27+
- name: Generate Xcode Project
28+
run: xcodegen generate
29+
30+
- name: Resolve Package Dependencies
31+
run: xcodebuild -resolvePackageDependencies -project OpenClip.xcodeproj
32+
33+
- name: Download Sparkle CLI Tools
34+
run: |
35+
mkdir -p /tmp/sparkle scripts/bin
36+
SPARKLE_SHA256="50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b"
37+
curl -fsSL "https://github.com/sparkle-project/Sparkle/releases/download/2.6.4/Sparkle-2.6.4.tar.xz" -o /tmp/Sparkle-2.6.4.tar.xz
38+
echo "$SPARKLE_SHA256 /tmp/Sparkle-2.6.4.tar.xz" | shasum -a 256 -c -
39+
tar xJ -f /tmp/Sparkle-2.6.4.tar.xz -C /tmp/sparkle
40+
cp -R /tmp/sparkle/bin/. scripts/bin/
41+
42+
- name: Build & Package Release (.zip, .dmg, & appcast.xml)
43+
id: build_package
44+
env:
45+
SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }}
46+
run: |
47+
VERSION="${GITHUB_REF_NAME#v}"
48+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
49+
./scripts/release_update.sh "$VERSION"
2850
2951
- name: Create GitHub Release
52+
id: create_release
3053
uses: softprops/action-gh-release@v2
3154
with:
3255
files: |
33-
build/OpenClip.zip
34-
build/OpenClip.dmg
56+
build/release/*.zip
57+
build/release/*.dmg
58+
build/release/appcast.xml
3559
draft: false
36-
prerelease: false
60+
prerelease: ${{ contains(github.ref, '-') }}
3761
generate_release_notes: true
62+
63+
- name: Update Homebrew Tap
64+
uses: mislav/bump-homebrew-formula-action@v3
65+
if: "!contains(github.ref, '-')"
66+
with:
67+
formula-name: openclip
68+
formula-path: Casks/openclip.rb
69+
homebrew-tap: ganeshmshetty/homebrew-tap
70+
download-url: https://github.com/ganeshmshetty/openclip/releases/download/${{ github.ref_name }}/OpenClip-v${{ steps.build_package.outputs.version }}.zip
71+
commit-message: "openclip ${{ steps.build_package.outputs.version }}"
72+
env:
73+
COMMITTER_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN || secrets.HOMEBREW_TAP_TOKEN }}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ DerivedData/
1616
.commandcode/
1717
codebase_bundle.xml
1818
graphify-out/
19-
web/
19+
web/
20+
scripts/bin/

AGENTS.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ OpenClip is a macOS floating-popup utility (Swift 6, macOS 14+, AppKit + SwiftUI
77
The Xcode project is **generated by XcodeGen** from `project.yml`, not edited by hand. After adding/removing `.swift` files, re-run `xcodegen generate` (the scripts re-run it automatically).
88

99
```bash
10-
git submodule update --init # populate Extensions/ catalog submodule (required once)
10+
git submodule update --init # populate Extensions/ catalog submodule (optional, for extension authoring)
1111
./scripts/dev_run.sh # build Debug + launch from DerivedData (logs: /tmp/openclip.log)
12-
./scripts/test.sh # full XCTest suite
13-
./scripts/test.sh ActionRegistryTests # single test class (flat Tests/OpenClipTests/, one target)
12+
./scripts/test.sh core # fast Core domain test suite (<1s)
13+
./scripts/test.sh # full test suite (0 skips, ~45s)
14+
./scripts/test.sh ActionRegistryTests # single test class
1415
./scripts/package_app.sh # Release build -> build/OpenClip.zip
1516
./scripts/clean.sh # wipe DerivedData/build caches
1617
```
@@ -20,7 +21,7 @@ All Swift is Swift 6 with `SWIFT_STRICT_CONCURRENCY: complete`. Targets (see `pr
2021
## Architecture hard rules
2122

2223
- **Core is pure** — no AppKit/SwiftUI imports in `Sources/Core/`, and the boundary is enforced by concept, not just import-grepping: UI-only presentation concerns (popup sizing/timing constants, chrome-style presentation metadata) live in `Sources/OpenClip/` — e.g. `PopupMetrics` holds popup/search sizing and `AIResultCardView` renders AI results natively — while `Core/Selection/Constants.swift` keeps only domain/runtime constants (timeouts, key codes, env vars, manifest keys). Platform side-effects live in `Sources/OpenClip/` (runtimes, `ActionResultHandler`, `DefaultActionFactory`).
23-
- **No direct `UserDefaults.standard`** in new code — route through `SettingsStore` + `SettingKey`. The only remaining raw access is the one-time `aiCloudAPIKey` migration in `AIServiceManager` (read-then-delete); don't add more. Secrets (AI API key, secret options) go to the **Keychain**, never UserDefaults.
24+
- **No direct `UserDefaults.standard`** in new code — route through `SettingsStore` + `SettingKey`. The only remaining raw access is the one-time `aiCloudAPIKey` migration in `AIServiceManager` (read-then-delete); don't add more. Secrets (AI API key, secret options) go to `SecretStore` (`~/.openclip/secrets.json` with 0600 POSIX permissions), never UserDefaults.
2425
- **`ActionCoordinator` is the composition root.** Managers report registry changes via `onRegister`/`onUnregister` callbacks only; nothing else touches `ActionRegistry.shared`.
2526
- **No `switch action.id` string-matching** in UI/presentation — use `ActionChrome` / `ConfigurableAction.preferenceIconName` instead.
2627
- Any new subprocess-spawning action must kill the child after `Constants.scriptTimeout` (30 s, the shared watchdog); shell/AppleScript/JS runtimes all join the existing `ShellProcessRunner` executor rather than spawning their own.
@@ -54,9 +55,9 @@ The **authoritative manifest / JS-bridge spec is `docs/developer-guide/AGENTS.md
5455
`Extensions/` is a **git submodule** (the openclip-extensions catalog); `Extensions/raw/` sits inside that submodule's working tree, so scaffolded extensions get committed there.
5556

5657
## Tests
57-
58+
5859
- `Tests/OpenClipTests/` is one flat target. Test classes that touch app singletons call `TestIsolation.reset()` in `setUp()`; store-backed tests use `MemorySettingsStore` rather than the real preferences domain.
59-
- A few deliberate live-integration tests remain and touch real system state (restored afterward): `TextRetrieverTests` (system clipboard), `KeychainActionOptionStoreTests` (real Keychain), `ScriptActionExecutionTests`/`ActionResultHandlerTests` (real subprocesses). Don't remove them without a seam.
60+
- All unit and integration tests are isolated with in-memory test doubles and temporary directories (including `TextRetrieverTests` and `SecretActionOptionStoreTests`), allowing the full suite to run cleanly in headless CI with zero skips.
6061

6162
## Docs & housekeeping
6263

CONTRIBUTING.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ Prefer the `scripts/` wrappers over raw `xcodebuild`/`xcodegen`. Full list is in
4848
| Task | Command |
4949
| :--- | :--- |
5050
| Quick compile gate | `timeout -k 5 60 xcodebuild -project OpenClip.xcodeproj -scheme OpenClip -destination 'platform=macOS' build` |
51-
| Full test suite | `timeout -k 10 60 ./scripts/test.sh` |
51+
| Core domain tests (<1s) | `./scripts/test.sh core` |
52+
| Full test suite (0 skips) | `timeout -k 10 60 ./scripts/test.sh` |
5253
| Single test class | `./scripts/test.sh SettingsStoreTests` |
5354
| Run the app | `./scripts/dev_run.sh` |
5455
| Package a Release | `./scripts/package_app.sh` |

Extensions

0 commit comments

Comments
 (0)