Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a8ad4d5d4 | ||
|
|
c2ac9341c8 | ||
|
|
1c310706e4 | ||
|
|
7f6c47d85c | ||
|
|
3b6323378e | ||
|
|
ad7b91cf91 | ||
|
|
8b931e3625 | ||
|
|
73b18dec5b | ||
|
|
3bc0d14a34 | ||
|
|
4aa2815c6e | ||
|
|
8596f15d52 |
@@ -35,7 +35,8 @@ jobs:
|
||||
# The job still succeeds quickly so `package`'s `needs:` stays satisfied.
|
||||
# Homebrew's xcodegen formula requires Xcode 15.3, which cannot be
|
||||
# installed on macOS 12 (#109). The script installs a pinned
|
||||
# prebuilt release instead.
|
||||
# prebuilt release instead. dmgbuild is not installed here — the
|
||||
# test job does not package (#95).
|
||||
- name: Ensure host tools
|
||||
if: "!(startsWith(github.ref, 'refs/tags/') && contains(github.ref_name, 'prerelease'))"
|
||||
run: scripts/ensure-host-tools.sh
|
||||
@@ -193,8 +194,10 @@ jobs:
|
||||
|
||||
# scripts/package-release.sh runs `xcodegen generate` and dmgbuild;
|
||||
# see build-and-test for why brew is not used on macOS 12 (#109).
|
||||
# INSTALL_DMGBUILD isolates dmgbuild in build/.venv-dmgbuild so
|
||||
# the test job never pip-installs it (#95).
|
||||
- name: Ensure host tools
|
||||
run: scripts/ensure-host-tools.sh
|
||||
run: INSTALL_DMGBUILD=1 scripts/ensure-host-tools.sh
|
||||
|
||||
- name: Package release
|
||||
run: scripts/package-release.sh
|
||||
|
||||
@@ -69,22 +69,24 @@ struct SettingsView: View {
|
||||
}
|
||||
|
||||
Section("Verification") {
|
||||
HStack {
|
||||
Text("Good ΔE ≤")
|
||||
TextField(
|
||||
"2.0",
|
||||
"Good ΔE ≤",
|
||||
value: $model.settings.deltaEGoodMax,
|
||||
format: .number
|
||||
)
|
||||
.frame(width: 60)
|
||||
Text("Warning ΔE ≤")
|
||||
.accessibilityIdentifier("settingsDeltaEGood")
|
||||
|
||||
TextField(
|
||||
"5.0",
|
||||
"Warning ΔE ≤",
|
||||
value: $model.settings.deltaEWarningMax,
|
||||
format: .number
|
||||
)
|
||||
.frame(width: 60)
|
||||
}
|
||||
.accessibilityIdentifier("settingsDeltaEWarning")
|
||||
|
||||
Text("Swatch and verify status use these as the green / amber cutoffs. Fail is anything above Warning.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
ForEach(model.validationErrors, id: \.self) { error in
|
||||
Text(error)
|
||||
.font(.caption)
|
||||
@@ -93,16 +95,12 @@ struct SettingsView: View {
|
||||
}
|
||||
|
||||
Section("Calibration") {
|
||||
HStack {
|
||||
Text("Stale after")
|
||||
TextField(
|
||||
"30",
|
||||
"Stale after (days)",
|
||||
value: $model.settings.calibrationStaleDays,
|
||||
format: .number
|
||||
)
|
||||
.frame(width: 60)
|
||||
Text("days")
|
||||
}
|
||||
.accessibilityIdentifier("settingsCalStaleDays")
|
||||
}
|
||||
|
||||
Section("Profile install") {
|
||||
@@ -143,6 +141,7 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.leading, 45)
|
||||
|
||||
Divider()
|
||||
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
/// Settings sheet UI tests (issue #165).
|
||||
///
|
||||
/// The Verification thresholds once shared one non-wrapping `HStack` and
|
||||
/// drew past the sheet's right clip on the macOS grouped `Form`. AX
|
||||
/// existence cannot see clipping (#163), so containment is asserted on
|
||||
/// real frame geometry against the sheet's bounds.
|
||||
///
|
||||
/// All three numeric fields also passed their default value as the
|
||||
/// `TextField` label; inside an `HStack` row that label renders inline —
|
||||
/// it is not a placeholder — producing "Stale after 30 [30] days". The
|
||||
/// fields are now direct `Form` children, so the descriptive label
|
||||
/// renders once in the label column and the box fills the control
|
||||
/// column; `testNumericFieldsCarryLabelsNotDuplicatedValues` pins it.
|
||||
@MainActor
|
||||
final class SettingsUITests: XCTestCase {
|
||||
|
||||
private var app: XCUIApplication!
|
||||
|
||||
override func setUp() async throws {
|
||||
continueAfterFailure = false
|
||||
app = XCUIApplication()
|
||||
app.launchEnvironment = ["ICCERY_UI_TESTING": "1"]
|
||||
app.launch()
|
||||
app.activate()
|
||||
}
|
||||
|
||||
override func tearDown() async throws {
|
||||
app?.terminate()
|
||||
app = nil
|
||||
}
|
||||
|
||||
/// Sheet content lives under `app.sheets`, outside the main window's
|
||||
/// a11y tree (Milestone2 pattern).
|
||||
private var sheet: XCUIElement {
|
||||
app.sheets.firstMatch
|
||||
}
|
||||
|
||||
private func openSettings() {
|
||||
let gear = app.buttons["openSettingsBtn"]
|
||||
XCTAssertTrue(gear.waitForExistence(timeout: 10))
|
||||
gear.click()
|
||||
XCTAssertTrue(sheet.waitForExistence(timeout: 10))
|
||||
}
|
||||
|
||||
private func thresholdField(_ fieldID: String) -> XCUIElement {
|
||||
let field = sheet.textFields[fieldID]
|
||||
XCTAssertTrue(field.waitForExistence(timeout: 10), "missing \(fieldID)")
|
||||
return field
|
||||
}
|
||||
|
||||
private func replaceFieldValue(_ field: XCUIElement, with text: String) {
|
||||
field.click()
|
||||
app.typeKey("a", modifierFlags: .command)
|
||||
field.typeText(text)
|
||||
}
|
||||
|
||||
private func waitForSheetDismiss(timeout: TimeInterval = 10) {
|
||||
let deadline = Date().addingTimeInterval(timeout)
|
||||
while Date() < deadline {
|
||||
if !sheet.exists { return }
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
|
||||
}
|
||||
XCTAssertFalse(sheet.exists, "Expected sheet to disappear")
|
||||
}
|
||||
|
||||
/// Both ΔE rows must render fully inside the 560×620 sheet: the
|
||||
/// label-column `StaticText`s and the control-column fields all sit
|
||||
/// within the sheet bounds, the two fields share the Form's control
|
||||
/// column margin, and the Warning row sits below the Good row so the
|
||||
/// two cannot overlap on one clipped line.
|
||||
///
|
||||
/// The numeric fields are direct `Form` children, so macOS lifts
|
||||
/// each `TextField` label into the right-aligned label column and
|
||||
/// the editable box fills the control column — the same layout the
|
||||
/// Pickers use. The control column ends only ~3.5 pt inside the
|
||||
/// sheet (PopUpButtons reach it too), so the right-edge assertion is
|
||||
/// "inside the sheet", not the older 12 pt compact-field inset.
|
||||
func testVerificationRowsStayInsideSheet() throws {
|
||||
openSettings()
|
||||
|
||||
let goodField = thresholdField("settingsDeltaEGood")
|
||||
let warningField = thresholdField("settingsDeltaEWarning")
|
||||
|
||||
// Labels render as sibling staticTexts in the label column.
|
||||
let goodLabel = sheet.staticTexts["Good ΔE ≤"]
|
||||
let warningLabel = sheet.staticTexts["Warning ΔE ≤"]
|
||||
XCTAssertTrue(goodLabel.waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(warningLabel.waitForExistence(timeout: 5))
|
||||
|
||||
// Left boundary: labels must be inside the sheet with >=12 pt inset
|
||||
XCTAssertTrue(goodLabel.frame.minX >= sheet.frame.minX + 12.0)
|
||||
XCTAssertTrue(warningLabel.frame.minX >= sheet.frame.minX + 12.0)
|
||||
|
||||
// Right boundary: fields must not draw past the sheet's clip
|
||||
XCTAssertTrue(
|
||||
goodField.frame.maxX <= sheet.frame.maxX,
|
||||
"Good ΔE field clips the sheet's right edge")
|
||||
XCTAssertTrue(
|
||||
warningField.frame.maxX <= sheet.frame.maxX,
|
||||
"Warning ΔE field clips the sheet's right edge")
|
||||
|
||||
// Vertical separation
|
||||
XCTAssertTrue(
|
||||
warningField.frame.minY > goodField.frame.minY,
|
||||
"thresholds must be two separate rows")
|
||||
|
||||
// Both threshold fields align at the same control column margin
|
||||
XCTAssertTrue(
|
||||
abs(goodField.frame.minX - warningField.frame.minX) <= 1.0,
|
||||
"Good and Warning ΔE fields should align at the same column margin")
|
||||
|
||||
// Other labels must not overflow the left boundary
|
||||
let defaultInstLabel = sheet.staticTexts["Default instrument"]
|
||||
XCTAssertTrue(defaultInstLabel.waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(
|
||||
defaultInstLabel.frame.minX >= sheet.frame.minX + 12.0,
|
||||
"Default instrument label must not overflow left edge")
|
||||
|
||||
let bundledSidecarsLabel = sheet.staticTexts["Bundled sidecars"]
|
||||
XCTAssertTrue(bundledSidecarsLabel.waitForExistence(timeout: 5))
|
||||
XCTAssertTrue(
|
||||
bundledSidecarsLabel.frame.minX >= sheet.frame.minX + 12.0,
|
||||
"Bundled sidecars label must not overflow left edge")
|
||||
|
||||
sheet.buttons["Cancel"].click()
|
||||
waitForSheetDismiss(timeout: 5)
|
||||
}
|
||||
|
||||
/// `warning <= good` fails `AppSettings.validate()` and keeps the
|
||||
/// sheet open with the contract error text; restoring valid values
|
||||
/// lets Save dismiss (issue #165 acceptance, strings are the #5
|
||||
/// contract).
|
||||
func testDeltaEValidationBlocksSaveThenValidSaveDismisses() throws {
|
||||
openSettings()
|
||||
|
||||
replaceFieldValue(thresholdField("settingsDeltaEWarning"), with: "1")
|
||||
sheet.buttons["Save"].click()
|
||||
|
||||
let error = sheet.staticTexts[
|
||||
"Good ΔE threshold must be strictly less than the warning threshold."
|
||||
]
|
||||
XCTAssertTrue(error.waitForExistence(timeout: 10))
|
||||
XCTAssertTrue(sheet.exists, "invalid ΔE must not dismiss the sheet")
|
||||
|
||||
replaceFieldValue(thresholdField("settingsDeltaEWarning"), with: "5")
|
||||
sheet.buttons["Save"].click()
|
||||
waitForSheetDismiss(timeout: 10)
|
||||
}
|
||||
|
||||
/// macOS renders a `TextField`'s first argument as a label, not a
|
||||
/// placeholder — inside the old `HStack` rows it drew inline, so the
|
||||
/// sheet read "Stale after 30 [30] days" / "Good ΔE ≤ 2.0 [2.0]".
|
||||
/// As direct `Form` children each label now renders exactly once, in
|
||||
/// the label column; no `staticText` may echo the field's value.
|
||||
func testNumericFieldsCarryLabelsNotDuplicatedValues() throws {
|
||||
openSettings()
|
||||
|
||||
// (identifier, label-column text, rendered default value, the
|
||||
// literal that used to double-render as the field's label)
|
||||
// `value:` shows the formatted number — 2.0 renders as "2".
|
||||
let rows: [(id: String, label: String, value: String, dup: String)] = [
|
||||
("settingsDeltaEGood", "Good ΔE ≤", "2", "2.0"),
|
||||
("settingsDeltaEWarning", "Warning ΔE ≤", "5", "5.0"),
|
||||
("settingsCalStaleDays", "Stale after (days)", "30", "30"),
|
||||
]
|
||||
|
||||
for spec in rows {
|
||||
let field = sheet.textFields[spec.id]
|
||||
XCTAssertTrue(field.waitForExistence(timeout: 10), "missing \(spec.id)")
|
||||
XCTAssertEqual(
|
||||
field.value as? String, spec.value,
|
||||
"\(spec.id) default value changed unexpectedly")
|
||||
XCTAssertTrue(
|
||||
sheet.staticTexts[spec.label].waitForExistence(timeout: 5),
|
||||
"\(spec.id) must render \"\(spec.label)\" once in the label column")
|
||||
for ghost in Set([spec.value, spec.dup]) {
|
||||
XCTAssertFalse(
|
||||
sheet.staticTexts[ghost].exists,
|
||||
"\(spec.id) must not render \"\(ghost)\" as a second label")
|
||||
}
|
||||
XCTAssertTrue(
|
||||
field.frame.maxX <= sheet.frame.maxX,
|
||||
"\(spec.id) field clips the sheet's right edge")
|
||||
}
|
||||
|
||||
sheet.buttons["Cancel"].click()
|
||||
waitForSheetDismiss(timeout: 5)
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -25,7 +25,7 @@ Cone-only mark for window/taskbar. Raster set:
|
||||
|
||||
| File | Use |
|
||||
|------|-----|
|
||||
| `icons/dmg-background.png` (+ `@2x`, `.svg`) | macOS DMG window (ice cream / wordmark scene). Headless `dmgbuild` after #189 |
|
||||
| `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). |
|
||||
| `icons/wix-banner.bmp`, `wix-dialog.bmp` | MSI |
|
||||
| `icons/nsis-header.bmp`, `nsis-sidebar.bmp` | NSIS |
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ targets:
|
||||
- ICCery.entitlements
|
||||
- ICCery.Debug.entitlements
|
||||
- Argyll
|
||||
- dmg-background.png
|
||||
- dmg-background@2x.png
|
||||
- path: Resources/Argyll
|
||||
type: folder
|
||||
dependencies:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
# scripts/dmgbuild-settings.py
|
||||
#
|
||||
# dmgbuild settings for ICCery. Set DMG_APP, DMG_FILENAME and DMG_VOLUME_NAME
|
||||
# in the environment, or accept the defaults. Background art can be supplied
|
||||
# later by placing a PNG at Resources/dmg-background.png and setting
|
||||
# DMG_BACKGROUND.
|
||||
# dmgbuild settings for ICCery. scripts/package-release.sh exports
|
||||
# DMG_APP, DMG_FILENAME, DMG_VOLUME_NAME, and DMG_BACKGROUND (a
|
||||
# HiDPI TIFF). A missing background is a hard error — a grey
|
||||
# Finder window is not an acceptable release artefact (#95).
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -23,15 +23,23 @@ if not app_path or not app_path.endswith('.app') or not os.path.isdir(app_path):
|
||||
|
||||
files = [app_path]
|
||||
|
||||
# Background art is optional. If the referenced PNG does not exist, fall back
|
||||
# to a plain window. See docs/23-assets.md for the DMG background spec.
|
||||
background = os.environ.get('DMG_BACKGROUND', 'Resources/dmg-background.png')
|
||||
if background and not os.path.exists(background):
|
||||
background = None
|
||||
# Finder on Sonoma+ is picky about PNG-with-alpha window pictures and
|
||||
# about classic Alias Manager blobs. package-release.sh always passes
|
||||
# a flattened HiDPI TIFF as DMG_BACKGROUND. dmgbuild 1.6.7 (bookmark
|
||||
# .DS_Store) needs Python >= 3.10, which the Monterey runner does not
|
||||
# have; 1.6.5 + TIFF is what CI can ship (#95).
|
||||
background = os.environ.get('DMG_BACKGROUND', '')
|
||||
if not background or not os.path.isfile(background):
|
||||
sys.stderr.write(
|
||||
'error: DMG_BACKGROUND must point at an existing image '
|
||||
'(got %r)\n' % background)
|
||||
sys.exit(1)
|
||||
|
||||
icon = None
|
||||
|
||||
# Window size is enough for the app icon and the Applications alias.
|
||||
# Bitmap is slightly larger than this rect so title-bar chrome on
|
||||
# 14+ does not crop the wordmark.
|
||||
window_rect = ((100, 100), (660, 400))
|
||||
|
||||
# Use icon view without extra chrome.
|
||||
|
||||
@@ -2,19 +2,79 @@
|
||||
# scripts/ensure-host-tools.sh
|
||||
#
|
||||
# Bootstrap host tools needed by CI on the macOS 12 runner:
|
||||
# - xcodegen: pinned prebuilt release from GitHub (Homebrew's current
|
||||
# formula requires Xcode 15.3, which cannot be installed on macOS 12).
|
||||
# - dmgbuild: via pip3 (used by scripts/package-release.sh).
|
||||
# - xcodegen: always. Pinned prebuilt release from GitHub (Homebrew's
|
||||
# current formula requires Xcode 15.3, which cannot be installed on
|
||||
# macOS 12).
|
||||
# - dmgbuild: only when INSTALL_DMGBUILD=1 or --dmgbuild. Isolated in
|
||||
# build/.venv-dmgbuild so the test job never pip-installs it.
|
||||
#
|
||||
# Safe to run repeatedly: existing tools are left alone.
|
||||
# dmgbuild 1.6.6+, ds_store 1.3.2+ and mac_alias 2.2.3 declare
|
||||
# Requires-Python >= 3.10. The wheels are py3-none-any and run on the
|
||||
# runner's 3.9; PIP_IGNORE_REQUIRES_PYTHON is required or pip will only
|
||||
# offer 1.6.5 and keep a cached venv on that version (#95).
|
||||
# pip itself is capped at <26.1: 26.1+ needs Python 3.10.
|
||||
#
|
||||
# Safe to run repeatedly: existing tools are left alone unless the
|
||||
# dmgbuild pin is not met.
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)"
|
||||
XCODEGEN_VERSION="2.38.0"
|
||||
INSTALL_ROOT="${XCODEGEN_HOME:-$HOME/.local/xcodegen/$XCODEGEN_VERSION}"
|
||||
VENV="$ROOT/build/.venv-dmgbuild"
|
||||
DMGBUILD_PIN="1.6.7"
|
||||
|
||||
echo "==> Ensuring dmgbuild"
|
||||
python3 -c "import dmgbuild" 2>/dev/null || pip3 install dmgbuild
|
||||
INSTALL_DMGBUILD="${INSTALL_DMGBUILD:-0}"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dmgbuild) INSTALL_DMGBUILD=1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$INSTALL_DMGBUILD" = "1" ]; then
|
||||
echo "==> Ensuring dmgbuild==$DMGBUILD_PIN in $VENV"
|
||||
mkdir -p "$ROOT/build"
|
||||
# pip 26.1+ requires Python 3.10 (dataclass slots). A leftover
|
||||
# `pip install --upgrade pip` on this 3.9 venv installed 26.2.1 and
|
||||
# the next pip invocation crashed. Recreate if pip is already dead.
|
||||
if [ -x "$VENV/bin/python" ] \
|
||||
&& ! "$VENV/bin/python" -m pip --version >/dev/null 2>&1; then
|
||||
echo "==> venv pip is broken; recreating $VENV"
|
||||
rm -rf "$VENV"
|
||||
fi
|
||||
if [ ! -x "$VENV/bin/python" ]; then
|
||||
python3 -m venv "$VENV"
|
||||
fi
|
||||
# Without this, pip on Python 3.9 hides 1.6.6+ and leaves 1.6.5.
|
||||
PIP_IGNORE_REQUIRES_PYTHON=1
|
||||
export PIP_IGNORE_REQUIRES_PYTHON
|
||||
"$VENV/bin/python" -m pip install --upgrade 'pip>=24.3,<26.1'
|
||||
"$VENV/bin/python" -m pip install --upgrade --force-reinstall \
|
||||
"dmgbuild==$DMGBUILD_PIN" \
|
||||
'ds_store>=1.3.3' \
|
||||
'mac_alias>=2.2.3'
|
||||
"$VENV/bin/python" -c 'from importlib.metadata import version
|
||||
print("dmgbuild", version("dmgbuild"))
|
||||
print("ds_store", version("ds_store"))
|
||||
print("mac_alias", version("mac_alias"))
|
||||
parts=[]
|
||||
for p in version("dmgbuild").split("."):
|
||||
try:
|
||||
parts.append(int("".join(c for c in p if c.isdigit()) or "0"))
|
||||
except ValueError:
|
||||
parts.append(0)
|
||||
parts += [0, 0, 0]
|
||||
raise SystemExit(0 if tuple(parts[:3]) >= (1, 6, 7) else 1)
|
||||
'
|
||||
if [ -n "${GITHUB_PATH:-}" ]; then
|
||||
echo "$VENV/bin" >> "$GITHUB_PATH"
|
||||
fi
|
||||
PATH="$VENV/bin:$PATH"
|
||||
export PATH
|
||||
else
|
||||
echo "==> Skipping dmgbuild (set INSTALL_DMGBUILD=1 for the package job)"
|
||||
fi
|
||||
|
||||
if command -v xcodegen >/dev/null 2>&1; then
|
||||
echo "==> xcodegen already on PATH: $(xcodegen --version)"
|
||||
|
||||
@@ -103,19 +103,39 @@ EOF
|
||||
scripts/verify-sidecar-signatures.sh "$APP"
|
||||
fi
|
||||
|
||||
echo "==> Installing / locating dmgbuild"
|
||||
echo "==> Locating dmgbuild"
|
||||
# The package CI job already ran INSTALL_DMGBUILD=1 ensure-host-tools.sh,
|
||||
# which created build/.venv-dmgbuild and prepended it to PATH. Local
|
||||
# runs bootstrap the same venv if dmgbuild is missing.
|
||||
VENV="$ROOT/build/.venv-dmgbuild"
|
||||
if ! command -v dmgbuild >/dev/null 2>&1; then
|
||||
VENV="$ROOT/build/.venv-dmgbuild"
|
||||
if [ ! -d "$VENV/bin" ]; then
|
||||
python3 -m venv "$VENV"
|
||||
"$VENV/bin/pip" install --upgrade pip
|
||||
"$VENV/bin/pip" install dmgbuild
|
||||
if [ ! -x "$VENV/bin/dmgbuild" ]; then
|
||||
INSTALL_DMGBUILD=1 "$ROOT/scripts/ensure-host-tools.sh" --dmgbuild
|
||||
fi
|
||||
PATH="$VENV/bin:$PATH"
|
||||
export PATH
|
||||
fi
|
||||
if ! command -v dmgbuild >/dev/null 2>&1; then
|
||||
echo "error: dmgbuild not available. Try 'python3 -m venv .venv && pip install dmgbuild'" >&2
|
||||
echo "error: dmgbuild not on PATH; run INSTALL_DMGBUILD=1 scripts/ensure-host-tools.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "dmgbuild $(command -v dmgbuild)"
|
||||
if [ -x "$VENV/bin/python" ]; then
|
||||
"$VENV/bin/python" -c 'from importlib.metadata import version; print("dmgbuild", version("dmgbuild"))'
|
||||
fi
|
||||
|
||||
PNG1X="$ROOT/Resources/dmg-background.png"
|
||||
PNG2X="$ROOT/Resources/dmg-background@2x.png"
|
||||
if [ ! -f "$PNG1X" ] || [ ! -f "$PNG2X" ]; then
|
||||
echo "error: missing $PNG1X or $PNG2X" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$ROOT/build"
|
||||
DMG_BACKGROUND="$ROOT/build/dmg-background.tiff"
|
||||
echo "==> Building HiDPI DMG background TIFF"
|
||||
tiffutil -cathidpicheck "$PNG1X" "$PNG2X" -out "$DMG_BACKGROUND"
|
||||
if [ ! -f "$DMG_BACKGROUND" ]; then
|
||||
echo "error: tiffutil did not write $DMG_BACKGROUND" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -128,6 +148,7 @@ VOLUME_NAME="ICCery ${VERSION}"
|
||||
DMG_APP="$APP" \
|
||||
DMG_FILENAME="$DMG" \
|
||||
DMG_VOLUME_NAME="$VOLUME_NAME" \
|
||||
DMG_BACKGROUND="$DMG_BACKGROUND" \
|
||||
dmgbuild -s scripts/dmgbuild-settings.py "$VOLUME_NAME" "$DMG"
|
||||
|
||||
echo "DMG: $PWD/$DMG"
|
||||
|
||||
Reference in New Issue
Block a user