-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProject.swift
More file actions
136 lines (129 loc) · 6.15 KB
/
Copy pathProject.swift
File metadata and controls
136 lines (129 loc) · 6.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import Foundation
import ProjectDescription
// The Mac App Store target, and only that. A SwiftPM executable can't carry a
// provisioning profile through App Store validation, so the store needs a real
// Xcode app target — but the DMG channel already works, so this deliberately
// does not touch it: make-app.sh and make-dmg.sh remain the Developer ID path.
//
// The store build must not contain Sparkle (Apple rejects apps that update
// themselves). That isn't enforced here by a flag someone can forget: this
// manifest declares no Sparkle package at all, so `#if canImport(Sparkle)` in
// Sources/NevermoreApp/Updater.swift is false and the updater compiles out.
// Adding Sparkle to this target would take a deliberate edit to this file.
/// Read from the same VERSION file `make-app.sh` uses, so the store build and
/// the DMG can't drift apart. Manifests are compiled and run, so this is an
/// ordinary file read at generate time.
let marketingVersion: String = {
let url = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.appendingPathComponent("Packages/NevermoreKit/VERSION")
guard let raw = try? String(contentsOf: url, encoding: .utf8) else {
fatalError("Packages/NevermoreKit/VERSION is missing — it is the single source of the marketing version")
}
let version = raw.trimmingCharacters(in: .whitespacesAndNewlines)
guard !version.isEmpty else { fatalError("Packages/NevermoreKit/VERSION is empty") }
return version
}()
/// App Store Connect requires a strictly increasing CFBundleVersion per upload.
/// The commit count is what `make-app.sh` uses; CI passes it in rather than
/// shelling out from a manifest. Well inside the store's uint32 limit.
let buildNumber = Environment.buildNumber.getString(default: "1")
/// Manual signing needs the profile's *name*, which only exists once the
/// profile has been created for com.brooksc.nevermore. Left empty, the target
/// uses automatic signing, which is what you want locally — Xcode will create
/// the profile the first time it archives.
let masProfileName = Environment.masProfileName.getString(default: "")
let appInfoPlist: [String: Plist.Value] = [
"CFBundleName": "Nevermore",
"CFBundleDisplayName": "Nevermore",
"CFBundleShortVersionString": .string(marketingVersion),
"CFBundleVersion": .string(buildNumber),
"CFBundleExecutable": "Nevermore",
"CFBundleIconFile": "AppIcon",
"CFBundlePackageType": "APPL",
"LSMinimumSystemVersion": "14.0",
// Required for the store; must match the primary category chosen in App
// Store Connect. Xcode warns on a build without it.
"LSApplicationCategoryType": "public.app-category.productivity",
"NSHighResolutionCapable": true,
"NSPrincipalClass": "NSApplication",
// Unsubscribe requests hit arbitrary, user-directed third-party endpoints,
// some of which publish http-only List-Unsubscribe URLs. ATS blocks those
// by default. The destinations are chosen by the mail sender, not the app,
// and reaching them is the app's whole job — see the App Review notes in
// MAS-RELEASE.md, which explain this to a reviewer along with the SSRF
// guard that validates every such URL and every redirect hop.
"NSAppTransportSecurity": ["NSAllowsArbitraryLoads": true],
]
/// Only set when a profile name is supplied; passing a profile to targets that
/// can't take one is how a manually-signed build starts failing in confusing
/// ways. Scoped to the app target for the same reason.
let signingSettings: SettingsDictionary =
masProfileName.isEmpty
? [:]
: [
"CODE_SIGN_STYLE": "Manual",
"CODE_SIGN_IDENTITY": "Apple Distribution",
"PROVISIONING_PROFILE_SPECIFIER": .string(masProfileName),
]
let project = Project(
name: "Nevermore",
organizationName: "Benjamin Brooks Cutter",
options: .options(automaticSchemesOptions: .disabled),
packages: [
.local(path: "Packages/NevermoreKit")
],
settings: .settings(
base: [
"DEVELOPMENT_TEAM": "SU999VT2G2",
"SWIFT_VERSION": "6.0",
"MARKETING_VERSION": .string(marketingVersion),
"CURRENT_PROJECT_VERSION": .string(buildNumber),
],
configurations: [
.debug(name: "Debug"),
.release(name: "Release"),
]
),
targets: [
.target(
name: "Nevermore",
destinations: .macOS,
product: .app,
bundleId: "com.brooksc.nevermore",
deploymentTargets: .macOS("14.0"),
infoPlist: .extendingDefault(with: appInfoPlist),
// The same sources make-app.sh builds — one app, two packagings,
// not two copies of the UI.
sources: ["Packages/NevermoreKit/Sources/NevermoreApp/**"],
resources: ["Packages/NevermoreKit/Resources/AppIcon.icns"],
entitlements: "Packages/NevermoreKit/Resources/Nevermore.entitlements",
dependencies: [
.package(product: "NevermoreKit")
],
// NEVERMORE_MAS compiles out anything the sandbox cannot run. Today
// that is the local HTTP server and its Settings section: the
// entitlements grant `network.client` but not `network.server`, so
// the listener could not bind, and the stdio bridge that would talk
// to it is not in this target's sources anyway. SwiftPM never
// defines this, so the DMG build keeps the feature.
//
// (Naming the bridge's product here, even in a comment, trips the
// test that pins this target to the app's sources alone.)
settings: .settings(
base: signingSettings.merging([
"SWIFT_ACTIVE_COMPILATION_CONDITIONS": "$(inherited) NEVERMORE_MAS"
]) { _, new in new }
)
)
],
schemes: [
.scheme(
name: "Nevermore",
shared: true,
buildAction: .buildAction(targets: ["Nevermore"]),
runAction: .runAction(configuration: "Debug", executable: "Nevermore"),
archiveAction: .archiveAction(configuration: "Release")
)
]
)