Requirements were still Xcode 15.4 / Swift 6. Point the product
at the user wiki, record host-arch tests, the dmgbuild venv, and
the current develop-first git workflow.
Unbounded pip upgrade installed 26.2.1, which uses
dataclass(slots=True) and crashes on Monterey 3.9. Recreate
the venv if pip is already broken, then pin pip>=24.3,<26.1
before force-installing dmgbuild 1.6.7.
Python 3.9 hides 1.6.6+ (Requires-Python >=3.10), so
dmgbuild>=1.6.5 left a cached 1.6.5 install. The wheels are
py3-none-any; install 1.6.7 with PIP_IGNORE_REQUIRES_PYTHON,
force-reinstall ds_store>=1.3.3 and mac_alias>=2.2.3, and fail
if the venv is still older than 1.6.7.
ensure-host-tools.sh always bootstraps xcodegen. dmgbuild now
lives in build/.venv-dmgbuild and is only installed when
INSTALL_DMGBUILD=1 (package job). The test job no longer
pip-installs dmgbuild, which is what failed on Python 3.9.
package-release.sh reuses that venv (or bootstraps it locally)
instead of creating a second copy.
The tools step runs on the test job. System Python is 3.9;
dmgbuild 1.6.6+ declares Requires-Python >=3.10, so pip only
lists 1.6.5 and the 1.6.7 floor failed the gate.
Install the newest wheel this interpreter accepts. Keep the
HiDPI TIFF background path and the hard-fail if art is missing.
Pin dmgbuild >= 1.6.7 so Finder gets a bookmark instead of a
Monterey-era Alias Manager blob. Feed a HiDPI TIFF from
tiffutil rather than the 1x RGBA PNG, and fail the package job
if the art is missing instead of shipping a grey window.
Exclude installer chrome from the app bundle; it is not an
in-app asset.
Native macOS frontend for printer ICC/ICM profiling. ICCery walks a user from chart generation through measurement, `colprof`, verification, and ColorSync install. It is **not** a colour engine.
Native macOS frontend for printer ICC/ICM profiling. ICCery walks a user from
chart generation through measurement, `colprof`, verification, and ColorSync
install. It is **not** a colour engine.
All measurement, chart generation, and profile mathematics live in the [Gronod ArgyllCMS 3.5.0 fork](https://git.i3omb.com/gronod/argyllcms), spawned as AGPLv3 child processes. The GUI never `dlopen`s or links Argyll.
**End-user guide:** the [repository wiki](https://git.i3omb.com/gronod/iccery-v2-mac/wiki)
covers every screen (Getting Started through Troubleshooting). This README is
for building, packaging, and contributing.
All measurement, chart generation, and profile mathematics live in the
| M10 | Studio workflow: gamut compare (#147), Spot Read (#148), project files (#149) shipped on `develop`; media library (#146) is in the tree, issue still open |
App Sandbox is **off**. Hardened Runtime is **on**. Entitlements live in
`ICCery.entitlements`.
## Build
@@ -49,7 +79,7 @@ cd iccery-v2-mac
git checkout develop
make fetch-argyll # Vendor/Argyll/macos-universal/, ad-hoc signed
make test# xcodegen + xcodebuild build test
make test# xcodegen + xcodebuild build test (host arch)
make universal # ARCHS='arm64 x86_64' ONLY_ACTIVE_ARCH=NO
```
@@ -62,9 +92,14 @@ xcodebuild test -scheme ICCery \
ARCHS="$(uname -m)"
```
`project.yml` sets `ARCHS: "$(ARCHS_STANDARD)"`, so a plain `xcodebuild test` (and `make test`) builds universal; the `ARCHS="$(uname -m)"` override narrows it to the host slice.
`project.yml` sets `ARCHS: "$(ARCHS_STANDARD)"`. CI and `make test` override
that with `ARCHS="$(uname -m)"` so unit/UI tests build the host slice only.
Fat binaries are `make universal` / `scripts/package-release.sh`.
Sidecars are **not** in git. `scripts/fetch-argyll.sh` pulls the latest (or`ARGYLL_RELEASE_TAG`) macOS-universal release from `gronod/argyllcms`, extracts to `Vendor/Argyll/macos-universal/`, ad-hoc signs every Mach-O, and fails if `codesign -dvv` or the `instlist` marker is missing.
Sidecars are **not** in git. `scripts/fetch-argyll.sh` pulls the latest (or
`ARGYLL_RELEASE_TAG`) macOS-universal release from `gronod/argyllcms`, extracts
to `Vendor/Argyll/macos-universal/`, ad-hoc signs every Mach-O, and fails if
`codesign -dvv` or the `instlist` marker is missing.
`ICCeryPrintKit` (issue #16, Quartz / AirPrint / TargetPrint) is v2.1 and is not in this tree.
`ICCeryPrintKit` (issue #16, Quartz / AirPrint / TargetPrint) is v2.1 and is
not in this tree.
## Architecture
- **Spawn, never link.** Tools resolve through `BinaryResolver` inside the bundle / `Vendor` tree. `$PATH` is not searched. `ARGYLL_NOT_INTERACTIVE=1` is always set.
- **`ProcessManager` actor** owns child lifetime. Streaming tools (`chartread`, `printcal`, etc.) use the event bus; one-shot tools use `runCaptured`. Exclusive `ProcessID` leases. Quit path: `q\n`, ~500 ms, kill; `killAll` on terminate.
- **Argv builders** in ICCeryCore (`TargenArgs`, `PrinttargArgs`, `ChartreadArgs`, `ColprofArgs`, `ApplycalArgs`, `IccgamutArgs`, `ProfcheckArgs`, `LpArgs`, …). UI must not concatenate flags.
- **Atomic artefacts.** Writes go to `*.tmp` then `replaceItemAt`. `applycal` must not replace the input profile on cancel or non-zero exit.
- **Concurrency.** View models are `@MainActor`. No blocking I/O on the main actor. SwiftUI `@Observable` for new state.
- **Print.** Unmanaged `lp` with ColorSync suppression (`AP_ColorMatchingMode` / `AP.ColorMatchingMode`). Captured `NSPrintPanel` options win over derived CUPS keys. Never `lp -o raw`.
- **Spawn, never link.** Tools resolve through `BinaryResolver` inside the
bundle / `Vendor` tree. `$PATH` is not searched. `ARGYLL_NOT_INTERACTIVE=1`
UI tests need an unlocked console (`IOConsoleLocked=false`). Mock Argyll / CUPS fixtures live under the test bundles; they must not be treated as proof that a real `.gam` / `.icc` was extracted.
CI (`.gitea/workflows/macos.yml`) runs `build-and-test` then `package` on
`develop` and on `v*` tags. Tags whose name contains `prerelease` skip the
test job and still package. `pull_request` is wired for **`develop` only**.
Hardware gates (real instrument, real printer, Gatekeeper-open `.dmg`) are manual and block release, not compile.
UI tests need an unlocked console (`IOConsoleLocked=false`). Mock Argyll /
CUPS fixtures live under the test bundles; they must not be treated as proof
that a real `.gam` / `.icc` was extracted.
Hardware gates (real instrument, real printer, Gatekeeper-open `.dmg`) are
manual and block release, not compile.
`ArgyllRunnerPrinttargTests.testSuccess` can flake if streaming stdout is
dropped on a fast mock exit; that is a `ProcessManager` drain race, not a
missing fixture.
## Instruments
@@ -156,11 +230,18 @@ Detected via bundled `instlist`:
- SpyderPrint (`p3`)
- SpectroScan (`SS`)
- DTP20 / 22 / 41 / 51
- XY tables (SpectroScan, i1iO) when the `instlist` name matches`/spectro\s?scan|i1io/i`
- XY tables (SpectroScan, i1iO) when the `instlist` name matches
`/spectro\s?scan|i1io/i`
## Docs
Normative spec is [`docs/`](docs/README.md). Implementation order:
| Where | Audience |
|---|---|
| [Wiki](https://git.i3omb.com/gronod/iccery-v2-mac/wiki) | End users — screens, workflow, troubleshooting |
| [`docs/`](docs/README.md) | Functional spec (normative for implementers) |
develop # integration; PRs land here unless a milestone branch is announced
main # protected release line (PR from develop)
feat/<issue>-<slug>
fix/<issue>-<slug>
```
Feature PRs target the current milestone branch, not `develop`. The milestone branch merges to `develop` when its issues are green. Completion PRs for issues #146–#149 target `milestone/m10-studio`; `milestone/m10-studio` merges into `develop` once all milestone gates pass. Do not open umbrella "bugfix" branches that mix tickets.
Open feature/fix PRs against **`develop`**. A `milestone/m…` integration
branch is used only while that milestone is assembling; `milestone/m10-studio`
has been merged and deleted. Do not open umbrella “bugfix” branches that mix
tickets.
`main` is push-protected and requires status check
`macOS CI / build-and-test (push)`. Protected **file** patterns on `main`
block PR merges that touch matching paths — do not set that field to `*`.
and personal evaluation only unless a separate grant says otherwise.
ArgyllCMS binaries fetched into `Vendor/Argyll/` are **AGPLv3**. They stay subprocess-isolated (stdin / stdout / stderr only). Linking them, or spawning via `$PATH`, is a licence break.
ArgyllCMS binaries fetched into `Vendor/Argyll/` are **AGPLv3**. They stay
| `Resources/dmg-background.png` (+ `@2x`; source `brand/dmg-background.svg`) | macOS DMG window. `scripts/package-release.sh` builds a HiDPI TIFF and passes it to `dmgbuild==1.6.7` from `build/.venv-dmgbuild` (created by `INSTALL_DMGBUILD=1 scripts/ensure-host-tools.sh`). Monterey Python 3.9 needs `PIP_IGNORE_REQUIRES_PYTHON=1` or pip will keep 1.6.5. Missing art is a hard fail (#95,#189). |
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.