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
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
release:
name: Build, sign, notarize and publish
runs-on: macos-26
timeout-minutes: 75
# Room for Apple: notarizing waits up to 90 minutes for the app and again for the disk image.
timeout-minutes: 210
env:
HAS_CERTIFICATE: ${{ secrets.SIGNING_CERTIFICATE != '' }}
steps:
Expand Down
5 changes: 0 additions & 5 deletions Casks/codec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,4 @@
"~/Library/Preferences/com.srozen.codec.plist",
"~/Library/Saved Application State/com.srozen.codec.savedState",
]

caveats <<~EOS
Codec isn't notarized by Apple yet, so macOS stops it the first time you open it, and again
after each upgrade. Open System Settings > Privacy & Security and choose Open Anyway beside Codec.
EOS
end
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Four themes (Codec, Arctic, Amber and Daylight) drawn through everything from th

Codec needs **macOS 26** or later, on Apple silicon or Intel.

**Download** the disk image from the [latest release](https://github.com/srozen/codec/releases/latest), open it and drag Codec to Applications. Codec isn't notarized by Apple yet, so the first time you open it, macOS stops it: choose **Open Anyway** in System Settings › Privacy & Security.
**Download** the disk image from the [latest release](https://github.com/srozen/codec/releases/latest), open it and drag Codec to Applications.

**Or with Homebrew:**

Expand Down
6 changes: 4 additions & 2 deletions Tests/CodecCoreTests/ShellIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ private final class ZshHarness: @unchecked Sendable {
log.events.withLock { $0.filter { $0.code == 133 && $0.payload == Array("B".utf8) }.map(\.cursor.col) }
}

/// Polls until `condition` holds or the timeout passes.
func wait(timeout: Duration = .seconds(15), until condition: () -> Bool) async -> Bool {
/// Polls until `condition` holds or the timeout passes. Longer on CI, whose runners can take more
/// than 15 seconds to start zsh while the whole suite runs; it only costs time when a test fails.
func wait(timeout: Duration = ProcessInfo.processInfo.environment["CI"] == nil ? .seconds(15) : .seconds(60),
until condition: () -> Bool) async -> Bool {
let deadline = ContinuousClock.now + timeout
while ContinuousClock.now < deadline {
if condition() { return true }
Expand Down
3 changes: 3 additions & 0 deletions Tests/CodecUITests/PaneContentsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ struct PaneContentsTests {
#expect(await waitUntil { pane.isAtCommandLine })
#expect(await run("echo first-run", in: pane))
#expect(await run("seq 1 5", in: pane))
// Its output is kept once the prompt after it is drawn, which can come a moment after the
// pane is back at the command line.
#expect(await waitUntil { pane.timeline.entries.last?.output != nil })
let contents = try #require(pane.savedContents())
pane.shutDown()
#expect(contents.timeline.entries.map(\.command) == ["echo first-run", "seq 1 5"])
Expand Down
8 changes: 7 additions & 1 deletion Tests/CodecUITests/TestSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,15 @@ func makeTestWindow(size: NSSize = NSSize(width: 700, height: 400)) -> NSWindow
return window
}

/// How long `waitUntil` waits by default. CI's runners start zsh far more slowly than a Mac at a desk
/// while the whole suite runs at once: since macOS 26.7 there, a shell has taken more than 15 seconds
/// to show its first prompt. Waits end as soon as their condition holds, so the longer limit only
/// costs time when a test fails.
let defaultWait: Duration = ProcessInfo.processInfo.environment["CI"] == nil ? .seconds(15) : .seconds(60)

/// Polls `condition` while letting the main queue run, until it holds or `timeout` passes.
@MainActor
func waitUntil(timeout: Duration = .seconds(15), _ condition: () -> Bool) async -> Bool {
func waitUntil(timeout: Duration = defaultWait, _ condition: () -> Bool) async -> Bool {
let deadline = ContinuousClock.now + timeout
while ContinuousClock.now < deadline {
if condition() { return true }
Expand Down
6 changes: 3 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

Download `Codec-<version>.dmg` from the [latest release](https://github.com/srozen/codec/releases/latest), open it and drag Codec to Applications.

Codec isn't notarized by Apple yet, so the first time you open it, macOS says it can't check it for malicious software. Open System Settings › Privacy & Security, scroll to Security, choose **Open Anyway** beside Codec and confirm. Updates Codec installs itself don't ask again; a version you download by hand does. Permissions you've given Codec, like Full Disk Access, carry over.
Codec is signed with a Developer ID and notarized by Apple, so macOS opens it like any app from the web. Versions up to 0.1.6 weren't: if you have one of those, download the latest by hand once (or upgrade it with Homebrew), as it can't install a notarized version itself, and macOS asks once more for the permissions you'd given it.

Or install it with Homebrew, from the cask in this repository (macOS asks the same way the first time):
Or install it with Homebrew, from the cask in this repository:

```bash
brew tap srozen/codec https://github.com/srozen/codec
Expand All @@ -22,7 +22,7 @@ brew install --cask srozen/codec/codec

Once a day, Codec asks GitHub for its latest release. When there's a newer one, it posts a notification, and Codec › Check for Updates… becomes Update to Codec 1.2.0…: choose it, or click the notification, to see what's new. **Skip This Version** stops it from announcing that one.

**Install** downloads the new version and gets it ready beside this one. Then **Install and Relaunch** puts it in place and reopens Codec with your windows, tabs and splits (programs running in them stop, and Codec asks first), or **Install on Quit** leaves it for the next time you quit. Codec only installs a version signed with the same certificate as the one you have, and it checks the download against the checksum GitHub lists for it.
**Install** downloads the new version and gets it ready beside this one. Then **Install and Relaunch** puts it in place and reopens Codec with your windows, tabs and splits (programs running in them stop, and Codec asks first), or **Install on Quit** leaves it for the next time you quit. Codec only installs a version signed by the same developer as the one you have, and it checks the download against the checksum GitHub lists for it.

Where Codec can't replace itself, the alert offers **Download** instead, which opens the new disk image: drag Codec to Applications, then reopen it. That's when it runs from the disk image or from Downloads, or from a folder you can't write to. If Homebrew installed Codec, **Upgrade** puts `brew upgrade --cask srozen/codec/codec` at the prompt for you to run instead.

Expand Down
2 changes: 1 addition & 1 deletion scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fi
notarize() {
local file="$1" staple="$2" result status id
echo "Notarizing $(basename "$file")…"
result="$(xcrun notarytool submit "$file" "${notary[@]}" --wait --timeout 30m --output-format json)"
result="$(xcrun notarytool submit "$file" "${notary[@]}" --wait --timeout 90m --output-format json)"
status="$(plutil -extract status raw -o - - <<<"$result")"
if [[ "$status" != "Accepted" ]]; then
id="$(plutil -extract id raw -o - - <<<"$result" || true)"
Expand Down
Loading