Skip to content

[6.1.1] Include fix for DocC permissions issue when Swift toolchain is installed as root #1197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion Sources/SwiftDocC/Utility/FileManagerProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ package protocol FileManagerProtocol: DataProvider {
/// Returns `true` if a file exists at the given path.
func fileExists(atPath: String) -> Bool
/// Copies a file from one location on the file-system to another.
func copyItem(at: URL, to: URL) throws
func _copyItem(at: URL, to: URL) throws // Use a different name than FileManager to work around https://github.com/swiftlang/swift-foundation/issues/1125
/// Moves a file from one location on the file-system to another.
func moveItem(at: URL, to: URL) throws
/// Creates a new file folder at the given location.
Expand Down Expand Up @@ -144,4 +144,25 @@ extension FileManager: FileManagerProtocol {
directories: Array( allContents[partitionIndex...] )
)
}

package func _copyItem(at source: URL, to destination: URL) throws {
// Call `NSFileManager/copyItem(at:to:)` and catch the error to workaround https://github.com/swiftlang/swift-foundation/issues/1125
do {
try copyItem(at: source, to: destination)
} catch let error as CocoaError {
// In Swift 6 on Linux, `FileManager/copyItems(at:to:)` raises an error _after_ successfully copying the files when it's moving over file attributes from the source to the destination.
// To workaround this issue, we check if the destination exist and the error wasn't that the destination _already_ existed.
if error.code != CocoaError.Code.fileWriteFileExists,
fileExists(atPath: destination.path)
{
// Ignore this error.
// The consequence is that the copied item may have some different attributes (creation date, owner, etc.) compared to the source.
// These attributes aren't critical for copying input files over to the output documentation archive.
return
}

// Otherwise, if this was any other error or if the destination file doesn't exist after calling `FileManager/copyItems(at:to:)`, re-throw the error to the caller.
throw error
}
}
}
4 changes: 2 additions & 2 deletions Sources/SwiftDocCTestUtilities/TestFileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ package class TestFileSystem: FileManagerProtocol {
return files.keys.contains(path)
}

package func copyItem(at source: URL, to destination: URL) throws {
package func _copyItem(at source: URL, to destination: URL) throws {
guard !disableWriting else { return }

filesLock.lock()
Expand All @@ -185,7 +185,7 @@ package class TestFileSystem: FileManagerProtocol {

let srcPath = srcURL.path

try copyItem(at: srcURL, to: dstURL)
try _copyItem(at: srcURL, to: dstURL)
files.removeValue(forKey: srcPath)

for (path, _) in files where path.hasPrefix(srcPath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extension AsyncAction {
// If a template directory has been provided, create the temporary build folder with its contents
// Ensure that the container exists
try? fileManager.createDirectory(at: targetURL.deletingLastPathComponent(), withIntermediateDirectories: false, attributes: nil)
try fileManager.copyItem(at: template, to: targetURL)
try fileManager._copyItem(at: template, to: targetURL)
} else {
// Otherwise, create an empty directory
try fileManager.createDirectory(at: targetURL, withIntermediateDirectories: true, attributes: nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer {
func copyAsset(_ asset: DataAsset, to destinationFolder: URL) throws {
for sourceURL in asset.variants.values where !sourceURL.isAbsoluteWebURL {
let assetName = sourceURL.lastPathComponent
try fileManager.copyItem(
try fileManager._copyItem(
at: sourceURL,
to: destinationFolder.appendingPathComponent(assetName, isDirectory: false)
)
Expand Down Expand Up @@ -143,7 +143,7 @@ struct ConvertFileWritingConsumer: ConvertOutputConsumer {
if fileManager.fileExists(atPath: targetFile.path) {
try fileManager.removeItem(at: targetFile)
}
try fileManager.copyItem(at: themeSettings, to: targetFile)
try fileManager._copyItem(at: themeSettings, to: targetFile)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,15 @@ class JSONEncodingRenderNodeWriter {
)

do {
try fileManager.copyItem(at: indexHTML, to: htmlTargetFileURL)
try fileManager._copyItem(at: indexHTML, to: htmlTargetFileURL)
} catch let error as NSError where error.code == NSFileWriteFileExistsError {
// We already have an 'index.html' file at this path. This could be because
// we're writing to an output directory that already contains built documentation
// or because we we're given bad input such that multiple documentation pages
// have the same path on the filesystem. Either way, we don't want this to error out
// so just remove the destination item and try the copy operation again.
try fileManager.removeItem(at: htmlTargetFileURL)
try fileManager.copyItem(at: indexHTML, to: htmlTargetFileURL)
try fileManager._copyItem(at: indexHTML, to: htmlTargetFileURL)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct MergeAction: AsyncAction {
try? fileManager.createDirectory(at: toDirectory, withIntermediateDirectories: false, attributes: nil)
for from in (try? fileManager.contentsOfDirectory(at: fromDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)) ?? [] {
// Copy each file or subdirectory
try fileManager.copyItem(at: from, to: toDirectory.appendingPathComponent(from.lastPathComponent))
try fileManager._copyItem(at: from, to: toDirectory.appendingPathComponent(from.lastPathComponent))
}
}
guard let jsonIndexData = fileManager.contents(atPath: archive.appendingPathComponent("index/index.json").path) else {
Expand Down Expand Up @@ -125,7 +125,7 @@ struct MergeAction: AsyncAction {
contents: RenderJSONEncoder.makeEncoder().encode(renderNode)
)
// It's expected that this will fail if combined archive doesn't support static hosting.
try? fileManager.copyItem(
try? fileManager._copyItem(
at: targetURL.appendingPathComponent("index.html"),
to: targetURL.appendingPathComponent("/documentation/index.html")
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ struct TransformForStaticHostingAction: AsyncAction {
// as we want to preserve anything intentionally left in the output URL by `setupOutputDirectory`
for sourceItem in try fileManager.contentsOfDirectory(at: rootURL, includingPropertiesForKeys: [], options:[.skipsHiddenFiles]) {
let targetItem = outputURL.appendingPathComponent(sourceItem.lastPathComponent)
try fileManager.copyItem(at: sourceItem, to: targetItem)
try fileManager._copyItem(at: sourceItem, to: targetItem)
}
}

Expand All @@ -81,7 +81,7 @@ struct TransformForStaticHostingAction: AsyncAction {
if fileManager.fileExists(atPath: target.path){
try fileManager.removeItem(at: target)
}
try fileManager.copyItem(at: source, to: target)
try fileManager._copyItem(at: source, to: target)
}

// Transform the indexHTML if needed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class TestFileSystemTests: XCTestCase {
func testCopyFiles() throws {
let fs = try makeTestFS()

try fs.copyItem(at: URL(string: "/main/nested/myfile1.txt")!, to: URL(string: "/main/myfile1.txt")!)
try fs._copyItem(at: URL(string: "/main/nested/myfile1.txt")!, to: URL(string: "/main/myfile1.txt")!)
XCTAssertEqual(fs.dump(), """
/
├─ main/
Expand All @@ -121,7 +121,7 @@ class TestFileSystemTests: XCTestCase {
func testCopyFolders() throws {
let fs = try makeTestFS()

try fs.copyItem(at: URL(string: "/main/nested")!, to: URL(string: "/copy")!)
try fs._copyItem(at: URL(string: "/main/nested")!, to: URL(string: "/copy")!)
XCTAssertEqual(fs.dump(), """
/
├─ copy/
Expand Down Expand Up @@ -286,7 +286,7 @@ class TestFileSystemTests: XCTestCase {
XCTAssertEqual(fs.contents(atPath: "/main/test.txt"), Data(base64Encoded: "TEST"))

// Copy a file and test the contents are identical with original
try fs.copyItem(at: URL(string: "/main/test.txt")!, to: URL(string: "/main/clone.txt")!)
try fs._copyItem(at: URL(string: "/main/test.txt")!, to: URL(string: "/main/clone.txt")!)
XCTAssertTrue(fs.contentsEqual(atPath: "/main/test.txt", andPath: "/main/clone.txt"))

_ = try fs.createFile(at: URL(string:"/main/notclone.txt")!, contents: Data(base64Encoded: "TESTTEST")!)
Expand Down