Make mac release rebuilds incremental and drop debug builds
This commit is contained in:
@@ -6,7 +6,7 @@ These are requirements, not preferences. Changing one needs the owner's agreemen
|
||||
|
||||
- Never deploy unless the owner asks for it in the current turn. A finished change is reported undeployed; the owner deploys it, or asks you to. Never run `go install ./cmd/byre` or restart the service on your own initiative, not even to check that a change works.
|
||||
- `docs/operations.md` describes the layout, data, service and APIs; keep it current with every change.
|
||||
- Never add tests. byre has no test suite; check a change with `go vet ./...`, `go generate ./web` (which type-checks the UI) and, when `mac/` changed, `swift build --package-path mac`.
|
||||
- Never add tests. byre has no test suite; check a change with `go vet ./...`, `go generate ./web` (which type-checks the UI) and, when `mac/` changed, `mac/bundle.sh` (release only; debug builds are refused).
|
||||
- The web UI (`web/src/`) and the macOS app (`mac/Sources/Byre/`) are two clients of the same views. A change to what a client shows or does, such as Markdown rendering, goes into both in the same task; never leave one client behind.
|
||||
|
||||
## Deploying
|
||||
|
||||
+5
-3
@@ -18,7 +18,9 @@ The first version copies the web client: the same two equal panes, tabs, labels,
|
||||
- `UserNotifications` for notifications.
|
||||
- Third-party code: Apple's `swift-openapi-generator` (with `swift-openapi-runtime` and `swift-openapi-urlsession`) for the API types and client, and SwiftTerm for the terminal.
|
||||
|
||||
The host has the Command Line Tools and no Xcode (`xcode-select --print-path` prints which), so the project is a Swift package under `mac/`, built with `swift build`, not an Xcode project. `mac/bundle.sh` builds the release configuration and assembles `mac/.build/byre.app` (the executable, `mac/Info.plist` with the fixed bundle identifier `run.fc.byre.mac`, the `web/fonts/` files and an `.icns` rendered from `web/favicon-tile.svg`) and signs it ad hoc; notification permission needs the bundle identifier. The app keeps its server origin in the `byre.server` default (`defaults read run.fc.byre.mac byre.server` prints it).
|
||||
The project is a Swift package under `mac/`, built with `swift build`, not an Xcode project. It builds with Xcode's toolchain: `xcode-select --print-path` must print `/Applications/Xcode.app/Contents/Developer`. With the Command Line Tools selected while Xcode is installed, Swift Build finds two toolchains for the Command Line Tools path and picks one at random on each run; the pick is part of the build request, so every change of pick rebuilds every target. `mac/bundle.sh` builds the release configuration and assembles `mac/.build/byre.app` (the executable, `mac/Info.plist` with the fixed bundle identifier `run.fc.byre.mac`, the `web/fonts/` files and an `.icns` rendered from `web/favicon-tile.svg`, cached as `mac/.build/byre.icns` until the SVG changes) and signs it ad hoc; notification permission needs the bundle identifier.
|
||||
|
||||
Only the release configuration is built. A debug build stops at an `#error` in `ByreApp.swift`, and even a failed one leaves the next release build recompiling every target, since the two configurations share `mac/.build`. The generated API client is its own target, `ByreAPI`, so an edit to the app recompiles only the `Byre` target. SwiftPM builds release targets whole-module, so that is one compile of the whole `Byre` target, and a one-file change still costs that. The app keeps its server origin in the `byre.server` default (`defaults read run.fc.byre.mac byre.server` prints it).
|
||||
|
||||
SwiftUI's own macros don't build here: the SDK declares `@State`, `@Entry`, `@Animatable` and the rest as macros implemented in the `SwiftUIMacros` plugin, which ships with Xcode and not with the Command Line Tools (`ls /Library/Developer/CommandLineTools/usr/lib/swift/host/plugins` lists the plugins there). `@State` is reached through its property-wrapper type instead, as `typealias ViewState = State` and `@ViewState var …`; other SwiftUI macros aren't used. `@Observable` and `@Bindable` (Observation's plugin, which the Command Line Tools have), `@Environment` and `@FocusState` work as usual.
|
||||
|
||||
@@ -28,7 +30,7 @@ Values the web client defines are generated into Swift, never copied by hand, so
|
||||
|
||||
| Value | Source | Swift |
|
||||
| --- | --- | --- |
|
||||
| API types and client | `api/openapi.yaml` | Generated at build time by the `swift-openapi-generator` plugin (`mac/Sources/Byre/openapi-generator-config.yaml`, idiomatic naming: `display_name` is `displayName`) from the symlink `mac/Sources/Byre/openapi.yaml` to the spec. The output lands in `mac/.build/plugins/outputs/`, not in git |
|
||||
| API types and client | `api/openapi.yaml` | Generated at build time into the `ByreAPI` target by the `swift-openapi-generator` plugin (`mac/Sources/ByreAPI/openapi-generator-config.yaml`, public access, idiomatic naming: `display_name` is `displayName`) from the symlink `mac/Sources/ByreAPI/openapi.yaml` to the spec. App files that use it `import ByreAPI`. The output lands in `mac/.build/plugins/outputs/`, not in git |
|
||||
| Palette, light and dark | Custom properties at the top of `web/style.css` (`:root` and its `prefers-color-scheme: dark` block) | `go generate ./mac` (`mac/palette`) writes `Palette.swift`: one dynamic `Color` per hex property, `--accent-tint` as `Palette.accentTint`, resolving light or dark with the window's appearance. Properties that aren't a hex colour (`--task`, `--shadow`, `--font`) are left out. The file is committed, so `swift build` needs no Go; a property missing from either block fails the generator |
|
||||
| Font | `web/fonts/` | The build script copies the woff2 files into the bundle; the app loads each with `CTFontManagerCreateFontDescriptorsFromURL` (see "Gaps in SwiftUI") |
|
||||
| App icon | `web/favicon-tile.svg` | The build script renders it to an `.icns` with `rsvg-convert` and `iconutil` |
|
||||
@@ -188,7 +190,7 @@ Platform differences the copy can't avoid, each with what the web client does an
|
||||
|
||||
## Building and checking
|
||||
|
||||
`swift build --package-path mac` is the compile check for changes under `mac/`, alongside the checks `AGENTS.md` already names. No tests are added. Installing the app is a separate action from deploying the byre service.
|
||||
`mac/bundle.sh` is the compile check for changes under `mac/`, alongside the checks `AGENTS.md` already names. No tests are added. Installing the app is a separate action from deploying the byre service.
|
||||
|
||||
## Not in version one
|
||||
|
||||
|
||||
+11
-4
@@ -11,14 +11,21 @@ let package = Package(
|
||||
.package(url: "https://github.com/apple/swift-openapi-urlsession", from: "1.1.0"),
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "Byre",
|
||||
.target(
|
||||
name: "ByreAPI",
|
||||
dependencies: [
|
||||
.product(name: "SwiftTerm", package: "SwiftTerm"),
|
||||
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
|
||||
.product(name: "OpenAPIURLSession", package: "swift-openapi-urlsession"),
|
||||
],
|
||||
plugins: [.plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator")]
|
||||
),
|
||||
.executableTarget(
|
||||
name: "Byre",
|
||||
dependencies: [
|
||||
"ByreAPI",
|
||||
.product(name: "SwiftTerm", package: "SwiftTerm"),
|
||||
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
|
||||
.product(name: "OpenAPIURLSession", package: "swift-openapi-urlsession"),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import Foundation
|
||||
import HTTPTypes
|
||||
import OpenAPIRuntime
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
#if DEBUG
|
||||
#error("debug builds are unsupported: build with mac/bundle.sh")
|
||||
#endif
|
||||
|
||||
typealias ViewState = State
|
||||
|
||||
@MainActor
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias TaskChanges = Components.Schemas.TaskChanges
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AppKit
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
enum ChatKind: Equatable {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AppKit
|
||||
import ByreAPI
|
||||
import OpenAPIRuntime
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias DirListing = Components.Schemas.DirListing
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AppKit
|
||||
import ByreAPI
|
||||
import OpenAPIRuntime
|
||||
import SwiftUI
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import OpenAPIRuntime
|
||||
import SwiftUI
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias Participant = Components.Schemas.Participant
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AppKit
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AppKit
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
enum TabBadge {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias Todo = Components.Schemas.Todo
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias FindingState = Components.Schemas.FindingState
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
import UserNotifications
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias AgentProcess = Components.Schemas.AgentProcess
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AppKit
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias WorkflowSummary = Components.Schemas.WorkflowSummary
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ByreAPI
|
||||
import SwiftUI
|
||||
|
||||
typealias WorkflowRun = Components.Schemas.WorkflowRun
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import OpenAPIRuntime
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
generate:
|
||||
- types
|
||||
- client
|
||||
accessModifier: internal
|
||||
accessModifier: public
|
||||
namingStrategy: idiomatic
|
||||
+14
-10
@@ -4,20 +4,24 @@ mac=$(cd "$(dirname "$0")" && pwd)
|
||||
root=$(dirname "$mac")
|
||||
build=$mac/.build
|
||||
app=$build/byre.app
|
||||
svg=$root/web/favicon-tile.svg
|
||||
icns=$build/byre.icns
|
||||
swift build --package-path "$mac" --configuration release
|
||||
bin=$(swift build --package-path "$mac" --configuration release --show-bin-path)
|
||||
if [ ! "$icns" -nt "$svg" ]; then
|
||||
iconset=$build/byre.iconset
|
||||
rm -rf "$iconset"
|
||||
mkdir -p "$iconset"
|
||||
for size in 16 32 128 256 512; do
|
||||
rsvg-convert --width $size --height $size --output "$iconset/icon_${size}x${size}.png" "$svg"
|
||||
rsvg-convert --width $((size * 2)) --height $((size * 2)) --output "$iconset/icon_${size}x${size}@2x.png" "$svg"
|
||||
done
|
||||
iconutil --convert icns --output "$icns" "$iconset"
|
||||
fi
|
||||
rm -rf "$app"
|
||||
mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources/fonts"
|
||||
cp "$bin/Byre" "$app/Contents/MacOS/Byre"
|
||||
cp "$build/release/Byre" "$app/Contents/MacOS/Byre"
|
||||
cp "$mac/Info.plist" "$app/Contents/Info.plist"
|
||||
cp "$root"/web/fonts/*.woff2 "$app/Contents/Resources/fonts/"
|
||||
iconset=$build/byre.iconset
|
||||
rm -rf "$iconset"
|
||||
mkdir -p "$iconset"
|
||||
for size in 16 32 128 256 512; do
|
||||
rsvg-convert --width $size --height $size --output "$iconset/icon_${size}x${size}.png" "$root/web/favicon-tile.svg"
|
||||
rsvg-convert --width $((size * 2)) --height $((size * 2)) --output "$iconset/icon_${size}x${size}@2x.png" "$root/web/favicon-tile.svg"
|
||||
done
|
||||
iconutil --convert icns --output "$app/Contents/Resources/byre.icns" "$iconset"
|
||||
cp "$icns" "$app/Contents/Resources/byre.icns"
|
||||
codesign --force --sign - "$app"
|
||||
echo "$app"
|
||||
|
||||
Reference in New Issue
Block a user