Skip to content

Commit 738f5f3

Browse files
authored
Fix Swift SDK bundles not working due to quarantine (#6426)
When using `swift experimental-sdk install` subcommand with bundle archives or bundles unpacked from archives downloaded manually in the browser, installed executables are quarantined and trigger an error message when used by `swift build` Steps To Reproduce: 1. Download a destination bundle archive 2. Unpack and install the bundle with `swift experimental-destination install` 3. Confirm that the destination is installed and note its ID with `swift experimental-destination list` 4. Build using the newly installed destination with `swift build --experimental-destination-selector <destination-id>` Actual result: Building with this destination triggers an error: "`swift-driver` cannot be opened because the developer cannot be verified." rdar://107392863
1 parent 678e683 commit 738f5f3

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

Sources/Basics/FileSystem/FileSystem+Extensions.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import struct TSCBasic.ByteString
1919
import struct TSCBasic.FileInfo
2020
import class TSCBasic.FileLock
2121
import enum TSCBasic.FileMode
22+
import enum TSCBasic.FileSystemAttribute
2223
import protocol TSCBasic.FileSystem
2324
import var TSCBasic.localFileSystem
2425
import protocol TSCBasic.WritableByteStream
@@ -71,8 +72,8 @@ extension FileSystem {
7172

7273
/// Returns `true` if a given path has a quarantine attribute applied if when file system supports this attribute.
7374
/// Returns `false` if such attribute is not applied or it isn't supported.
74-
public func hasQuarantineAttribute(_ path: AbsolutePath) -> Bool {
75-
self.hasQuarantineAttribute(path.underlying)
75+
public func hasAttribute(_ name: FileSystemAttribute, _ path: AbsolutePath) -> Bool {
76+
self.hasAttribute(name, path.underlying)
7677
}
7778

7879
/// Get the contents of the given directory, in an undefined order.

Sources/PackageModel/Destination.swift

+16
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ public enum DestinationError: Swift.Error {
5252
/// A destination with this artifact ID is already installed. Can't install a new bundle with this artifact,
5353
/// installed artifact IDs are expected to be unique.
5454
case destinationArtifactAlreadyInstalled(installedBundleName: String, newBundleName: String, artifactID: String)
55+
56+
#if os(macOS)
57+
/// Quarantine attribute should be removed by the `xattr` command from an installed bundle.
58+
case quarantineAttributePresent(bundlePath: AbsolutePath)
59+
#endif
5560
}
5661

5762
extension DestinationError: CustomStringConvertible {
@@ -93,6 +98,17 @@ extension DestinationError: CustomStringConvertible {
9398
`\(installedBundleName)`. Can't install a new bundle `\(newBundleName)` with this artifact, artifact IDs \
9499
are expected to be unique across all installed Swift SDK bundles.
95100
"""
101+
#if os(macOS)
102+
case .quarantineAttributePresent(let bundlePath):
103+
return """
104+
Quarantine attribute is present on a Swift SDK bundle at path `\(bundlePath)`. If you're certain that the \
105+
bundle was downloaded from a trusted source, you can remove the attribute with this command:
106+
107+
xattr -d -r -s com.apple.quarantine "\(bundlePath)"
108+
109+
and try to install this bundle again.
110+
"""
111+
#endif
96112
}
97113
}
98114
}

Sources/PackageModel/SwiftSDKBundle.swift

+12-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import Basics
1414

1515
import struct Foundation.URL
16+
import protocol TSCBasic.FileSystem
1617
import struct TSCBasic.RegEx
1718

1819
/// Represents an `.artifactbundle` on the filesystem that contains a Swift SDK.
@@ -120,7 +121,7 @@ public struct SwiftSDKBundle {
120121

121122
return selectedDestination
122123
}
123-
124+
124125
/// Installs a destination bundle from a given path or URL to a destinations installation directory.
125126
/// - Parameters:
126127
/// - bundlePathOrURL: A string passed on the command line, which is either an absolute or relative to a current
@@ -245,6 +246,13 @@ public struct SwiftSDKBundle {
245246
_ archiver: some Archiver,
246247
_ observabilityScope: ObservabilityScope
247248
) throws {
249+
#if os(macOS)
250+
// Check the quarantine attribute on bundles downloaded manually in the browser.
251+
guard !fileSystem.hasAttribute(.quarantine, bundlePath) else {
252+
throw DestinationError.quarantineAttributePresent(bundlePath: bundlePath)
253+
}
254+
#endif
255+
248256
let unpackedBundlePath = try unpackIfNeeded(
249257
bundlePath: bundlePath,
250258
destinationsDirectory: destinationsDirectory,
@@ -359,7 +367,8 @@ extension ArtifactsArchiveMetadata {
359367

360368
do {
361369
let destinations = try Destination.decode(
362-
fromFile: variantConfigurationPath, fileSystem: fileSystem, observabilityScope: observabilityScope
370+
fromFile: variantConfigurationPath, fileSystem: fileSystem,
371+
observabilityScope: observabilityScope
363372
)
364373

365374
variants.append(.init(metadata: variantMetadata, swiftSDKs: destinations))
@@ -378,7 +387,7 @@ extension ArtifactsArchiveMetadata {
378387
}
379388
}
380389

381-
extension Array where Element == SwiftSDKBundle {
390+
extension [SwiftSDKBundle] {
382391
/// Select a destination with a given artifact ID from a `self` array of available destinations.
383392
/// - Parameters:
384393
/// - id: artifact ID of the destination to look up.

0 commit comments

Comments
 (0)