Skip to content

Commit 717b3ce

Browse files
Merge pull request #29 from SwiftPackageIndex/fix-relative-path-zipping
Fix relative path zipping
2 parents 5edeb1e + 7915a2b commit 717b3ce

File tree

6 files changed

+91
-32
lines changed

6 files changed

+91
-32
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ jobs:
2828
image: swift:5.10.0-amazonlinux2
2929
steps:
3030
- name: Checkout
31-
uses: actions/checkout@v3
31+
uses: actions/checkout@v4
3232
- name: Run tests
3333
run: swift test

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
runs-on: ubuntu-latest
2828

2929
steps:
30-
- uses: actions/checkout@v3
30+
- uses: actions/checkout@v4
3131
with:
3232
# Fetch all history to ensure the version number (tag) is resolved to create the
3333
# version info during the build step.

Sources/DocUploadBundle/DocUploadBundle.swift

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,7 @@ public struct DocUploadBundle {
110110
let metadataURL = URL(fileURLWithPath: "\(workDir)/metadata.json")
111111
try JSONEncoder().encode(metadata).write(to: metadataURL)
112112

113-
switch method {
114-
case .library, .zipTool(workingDirectory: .some(_)):
115-
try Zipper.zip(paths: [metadataURL, URL(fileURLWithPath: sourcePath)], to: archiveURL, method: method)
116-
117-
case .zipTool(.none):
118-
// By default, run the zip tool in the working directory
119-
try Zipper.zip(paths: [metadataURL, URL(fileURLWithPath: sourcePath)], to: archiveURL, method: .zipTool(workingDirectory: workDir))
120-
}
113+
try Zipper.zip(paths: [metadataURL, URL(fileURLWithPath: sourcePath)], to: archiveURL, method: method)
121114

122115
return archiveURL.path
123116
}

Tests/DocUploadBundleTests/TempDir.swift renamed to Sources/DocUploadBundle/TempDir.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@ func withTempDir<T>(body: (String) async throws -> T) async throws -> T {
3232
let tmp = try TempDir()
3333
return try await body(tmp.path)
3434
}
35+
36+
func withTempDir<T>(body: (String) throws -> T) throws -> T {
37+
let tmp = try TempDir()
38+
return try body(tmp.path)
39+
}

Sources/DocUploadBundle/Zipper.swift

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,39 +21,63 @@ public enum Zipper {
2121
public static func zip(paths inputPaths: [URL], to outputPath: URL, method: Method = .library) throws {
2222
switch method {
2323
case .library:
24-
do { try Zip.zipFiles(paths: inputPaths, zipFilePath: outputPath, password: nil, progress: nil) }
25-
catch ZipError.fileNotFound { throw Error.fileNotFound }
26-
catch ZipError.unzipFail { throw Error.unzipFail }
27-
catch ZipError.zipFail { throw Error.zipFail }
28-
catch { throw Error.generic(reason: "\(error)") }
24+
do {
25+
try Zip.zipFiles(paths: inputPaths, zipFilePath: outputPath, password: nil, progress: nil)
26+
} catch let error as ZipError {
27+
switch error {
28+
case .fileNotFound: throw Error.fileNotFound
29+
case .unzipFail: throw Error.unzipFail
30+
case .zipFail: throw Error.zipFail
31+
}
32+
}
33+
catch {
34+
throw Error.generic(reason: "\(error)")
35+
}
2936

30-
case let .zipTool(cwd):
37+
case .zipTool:
3138
do {
32-
let process = Process()
33-
process.executableURL = zip
34-
process.arguments = ["-q", "-r", outputPath.path] + inputPaths.map(\.lastPathComponent)
35-
process.currentDirectoryURL = cwd.map(URL.init(fileURLWithPath:))
36-
try process.run()
37-
process.waitUntilExit()
39+
try withTempDir { tempDir in
40+
let tempURL = URL(fileURLWithPath: tempDir)
41+
// Copy inputs to tempDir
42+
for source in inputPaths {
43+
let target = tempURL.appendingPathComponent(source.lastPathComponent)
44+
try FileManager.default.copyItem(at: source, to: target)
45+
}
46+
47+
// Run zip
48+
let process = Process()
49+
process.executableURL = zip
50+
process.arguments = ["-q", "-r", outputPath.path] + inputPaths.map(\.lastPathComponent)
51+
process.currentDirectoryURL = tempURL
52+
try process.run()
53+
process.waitUntilExit()
54+
}
3855
} catch {
3956
throw Error.generic(reason: "\(error)")
4057
}
4158
}
4259
}
4360

4461
public static func unzip(from inputPath: URL, to outputPath: URL, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws {
45-
do { try Zip.unzipFile(inputPath, destination: outputPath, overwrite: true, password: nil, fileOutputHandler: fileOutputHandler) }
46-
catch ZipError.fileNotFound { throw Error.fileNotFound }
47-
catch ZipError.unzipFail { throw Error.unzipFail }
48-
catch ZipError.zipFail { throw Error.zipFail }
49-
catch { throw Error.generic(reason: "\(error)") }
62+
do {
63+
try Zip.unzipFile(inputPath, destination: outputPath, overwrite: true, password: nil, fileOutputHandler: fileOutputHandler)
64+
} catch let error as ZipError {
65+
switch error {
66+
case .fileNotFound: throw Error.fileNotFound
67+
case .unzipFail: throw Error.unzipFail
68+
case .zipFail: throw Error.zipFail
69+
}
70+
}
71+
catch {
72+
throw Error.generic(reason: "\(error)")
73+
}
5074
}
5175

5276
static let zip = URL(fileURLWithPath: "/usr/bin/zip")
5377

5478
public enum Method {
5579
case library
56-
case zipTool(workingDirectory: String? = nil)
80+
case zipTool
5781
}
5882

5983
public enum Error: Swift.Error {

Tests/DocUploadBundleTests/ZipTests.swift

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ final class ZipTests: XCTestCase {
2121

2222
func test_unzip() async throws {
2323
// Test basic unzip behaviour we expect from the library we use
24-
try await withTempDir { tempDir in
24+
try withTempDir { tempDir in
2525
let tempURL = URL(fileURLWithPath: tempDir)
2626
let zipFile = fixtureUrl(for: "out.zip")
2727
let outDir = tempURL.appendingPathComponent("out")
@@ -41,7 +41,7 @@ final class ZipTests: XCTestCase {
4141

4242
func test_zip_roundtrip() async throws {
4343
// Test basic zip roundtrip
44-
try await withTempDir { tempDir in
44+
try withTempDir { tempDir in
4545
// temp
4646
let tempURL = URL(fileURLWithPath: tempDir)
4747

@@ -92,7 +92,7 @@ final class ZipTests: XCTestCase {
9292
try XCTSkipIf(!FileManager.default.fileExists(atPath: Zipper.zip.path))
9393

9494
// Test basic zip roundtrip with the shellTool method
95-
try await withTempDir { tempDir in
95+
try withTempDir { tempDir in
9696
// temp
9797
let tempURL = URL(fileURLWithPath: tempDir)
9898

@@ -117,7 +117,7 @@ final class ZipTests: XCTestCase {
117117
try "c".write(to: fileC, atomically: true, encoding: .utf8)
118118

119119
let zipFile = tempURL.appendingPathComponent("out.zip")
120-
try Zipper.zip(paths: [fileA, subdir], to: zipFile, method: .zipTool(workingDirectory: tempDir))
120+
try Zipper.zip(paths: [fileA, subdir], to: zipFile, method: .zipTool)
121121
XCTAssert(FileManager.default.fileExists(atPath: zipFile.path))
122122

123123
do { // unzip what we zipped and check results
@@ -139,4 +139,41 @@ final class ZipTests: XCTestCase {
139139
}
140140
}
141141

142+
func test_zip_roundtrip_shellTool_relative_paths() async throws {
143+
try XCTSkipIf(!FileManager.default.fileExists(atPath: Zipper.zip.path))
144+
145+
// Test basic zip roundtrip with the shellTool method and relative paths
146+
try withTempDir { tempDir in
147+
// DocBundle components
148+
// metadataURL: tempDir/metadata.json
149+
// sourceURL: tempDir/.docs/owner/repo/ref
150+
// should be zipped as
151+
// - metadata.json
152+
// - ref
153+
// at the top level as relative paths.
154+
let tempURL = URL(fileURLWithPath: tempDir)
155+
let metadataURL = tempURL.appendingPathComponent("metadata.json")
156+
try "metadata".write(to: metadataURL, atomically: true, encoding: .utf8)
157+
let sourceURL = tempURL.appendingPathComponent("docs/owner/repo/ref")
158+
try FileManager.default.createDirectory(at: sourceURL, withIntermediateDirectories: true)
159+
let indexHTML = sourceURL.appendingPathComponent("index.html")
160+
try "index".write(to: indexHTML, atomically: true, encoding: .utf8)
161+
162+
// MUT
163+
let zipFile = tempURL.appendingPathComponent("out.zip")
164+
try Zipper.zip(paths: [metadataURL, sourceURL], to: zipFile, method: .zipTool)
165+
166+
do { // validate
167+
let unzipDir = tempURL.appendingPathComponent("unzip")
168+
try Zipper.unzip(from: zipFile, to: unzipDir)
169+
let metadataURL = unzipDir.appendingPathComponent("metadata.json")
170+
let indexHTML = unzipDir.appendingPathComponent("ref/index.html")
171+
XCTAssert(FileManager.default.fileExists(atPath: metadataURL.path))
172+
XCTAssert(FileManager.default.fileExists(atPath: indexHTML.path))
173+
XCTAssertEqual(try String(contentsOf: metadataURL), "metadata")
174+
XCTAssertEqual(try String(contentsOf: indexHTML), "index")
175+
}
176+
}
177+
}
178+
142179
}

0 commit comments

Comments
 (0)