Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39f81ae258 | ||
|
|
670dc1163f | ||
|
|
eb33ff8906 | ||
|
|
f9cee2c75f | ||
|
|
7a974911d8 | ||
|
|
891d70ec7c | ||
|
|
142d4bc111 | ||
|
|
6461a550c6 | ||
|
|
8b90260517 | ||
|
|
7924d20c8c |
3
.github/codecov.yml
vendored
3
.github/codecov.yml
vendored
@@ -1,2 +1,3 @@
|
||||
ignore:
|
||||
- Tests
|
||||
- Tests/PathTests/etc.swift
|
||||
- Tests/PathTests/TemporaryDirectory.swift
|
||||
|
||||
24
.github/workflows/cd.yml
vendored
Normal file
24
.github/workflows/cd.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: CD
|
||||
on:
|
||||
release:
|
||||
types: published
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: steven0351/publish-jazzy-docs@v1
|
||||
with:
|
||||
personal_access_token: ${{ secrets.PAT }}
|
||||
config: .github/jazzy.yml
|
||||
version: ${{ github.event.release.tag_name }}
|
||||
history: false
|
||||
|
||||
pods:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: pod trunk push --allow-warnings
|
||||
env:
|
||||
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
|
||||
VERSION: ${{ github.event.release.tag_name }}
|
||||
3
.github/workflows/checks.yml
vendored
3
.github/workflows/checks.yml
vendored
@@ -3,6 +3,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- Sources/**
|
||||
- Tests/**
|
||||
jobs:
|
||||
macOS:
|
||||
runs-on: macos-latest
|
||||
|
||||
90
.github/workflows/ci.yml
vendored
90
.github/workflows/ci.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: CI
|
||||
on: [pull_request]
|
||||
on: pull_request
|
||||
jobs:
|
||||
smoke:
|
||||
runs-on: macos-latest
|
||||
@@ -8,35 +8,22 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- run: swift test --generate-linuxmain
|
||||
- run: git diff --exit-code
|
||||
macOS:
|
||||
needs: smoke
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
xcode:
|
||||
#- 10.3 # Swift 5.0 (doesn’t work on GHA macOS image :-/)
|
||||
- 11.3 # Swift 5.1
|
||||
- ^11.4 # Swift 5.2
|
||||
- latest # Swift 5.3
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: setup-xcode
|
||||
uses: maxim-lobanov/setup-xcode@1.0
|
||||
with:
|
||||
xcode-version: ${{ matrix.xcode }}
|
||||
- run: swift --version
|
||||
- run: swift test --parallel
|
||||
otherOS:
|
||||
needs: smoke
|
||||
|
||||
apple:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
destination:
|
||||
- platform=iOS Simulator,OS=latest,name=iPhone 11
|
||||
- platform=iOS Simulator,OS=latest,name=iPhone 12
|
||||
- platform=tvOS Simulator,OS=latest,name=Apple TV
|
||||
- platform=macOS # for code-coverage
|
||||
# - platform=watchOS Simulator,OS=latest,name=Apple Watch Series 5 - 40mm
|
||||
# ^^ coming with Xcode 12.5 which is not yet available on GHA
|
||||
- platform=macOS
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 12.4
|
||||
- run: swift package generate-xcodeproj --enable-code-coverage
|
||||
- uses: sersoft-gmbh/xcodebuild-action@v1
|
||||
with:
|
||||
@@ -45,33 +32,52 @@ jobs:
|
||||
destination: ${{ matrix.destination }}
|
||||
action: test
|
||||
- uses: codecov/codecov-action@v1
|
||||
watchOS:
|
||||
needs: smoke
|
||||
runs-on: macos-latest
|
||||
|
||||
linux-swift-4:
|
||||
name: linux (4.2)
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: swift package generate-xcodeproj --enable-code-coverage
|
||||
- uses: sersoft-gmbh/xcodebuild-action@v1
|
||||
- uses: fwal/setup-swift@v1
|
||||
with:
|
||||
project: Path.swift.xcodeproj
|
||||
scheme: Path.swift-Package
|
||||
destination: platform=watchOS Simulator,OS=latest,name=Apple Watch Series 5 - 40mm
|
||||
action: build
|
||||
swift-version: 4.2
|
||||
- uses: actions/checkout@v2
|
||||
- run: swift test --parallel
|
||||
|
||||
linux:
|
||||
needs: smoke
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-18.04
|
||||
strategy:
|
||||
matrix:
|
||||
swift:
|
||||
# - 4.0.3 fails for some reason
|
||||
- 4.2
|
||||
- 5.0.3
|
||||
- 5.1
|
||||
- 5.2
|
||||
# - 5.3 not available yet sigh
|
||||
- '5.0'
|
||||
- '5.1'
|
||||
- '5.2'
|
||||
- '5.3'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: fwal/setup-swift@v1
|
||||
with:
|
||||
swift-version: ${{ matrix.swift }}
|
||||
- uses: actions/checkout@v2
|
||||
- run: swift test --parallel --enable-code-coverage
|
||||
- name: Generate Coverage Report
|
||||
run: |
|
||||
sudo apt-get -qq update && sudo apt-get -qq install llvm-10
|
||||
export b=$(swift build --show-bin-path) && llvm-cov-10 \
|
||||
export -format lcov \
|
||||
-instr-profile=$b/codecov/default.profdata \
|
||||
--ignore-filename-regex='\.build/' \
|
||||
$b/Path.swiftPackageTests.xctest \
|
||||
> info.lcov
|
||||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: ./info.lcov
|
||||
|
||||
# code coverage fails with 5.4 for some reason
|
||||
linux-swift-5-4:
|
||||
name: linux (5.4)
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: fwal/setup-swift@v1
|
||||
with:
|
||||
swift-version: 5.4
|
||||
- uses: actions/checkout@v2
|
||||
- run: swift test --parallel
|
||||
|
||||
16
Path.swift.podspec
Normal file
16
Path.swift.podspec
Normal file
@@ -0,0 +1,16 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Path.swift"
|
||||
spec.version = ENV['VERSION'] || "0.0.1"
|
||||
spec.summary = "Delightful, robust, cross-platform and chainable file-pathing functions."
|
||||
spec.homepage = "https://github.com/mxcl/Path.swift"
|
||||
spec.license = "Unlicense"
|
||||
spec.author = { "Max Howell" => "mxcl@me.com" }
|
||||
spec.source = { :git => "https://github.com/mxcl/Path.swift.git", :tag => "#{spec.version}" }
|
||||
spec.source_files = "Sources/*.swift"
|
||||
spec.swift_versions = ['4.2', '5']
|
||||
spec.module_name = 'Path'
|
||||
spec.osx.deployment_target = '10.10'
|
||||
spec.ios.deployment_target = '8.0'
|
||||
spec.tvos.deployment_target = '9.0'
|
||||
spec.watchos.deployment_target = '2.0'
|
||||
end
|
||||
26
README.md
26
README.md
@@ -45,16 +45,13 @@ try Bundle.resources.helper.copy(into: Path.root.usr.local.bin).chmod(0o500)
|
||||
We emphasize safety and correctness, just like Swift, and also (again like
|
||||
Swift), we provide a thoughtful and comprehensive (yet concise) API.
|
||||
|
||||
# Support mxcl
|
||||
# Sponsor @mxcl
|
||||
|
||||
Hi, I’m Max Howell and I have written a lot of open source software—generally
|
||||
a good deal of my free time 👨🏻💻.
|
||||
a good deal of my free time 👨🏻💻. Sponsorship helps me justify creating new open
|
||||
source and maintaining it. Thank you.
|
||||
|
||||
<a href="https://www.patreon.com/mxcl">
|
||||
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
|
||||
</a>
|
||||
|
||||
[Other donation/tipping options](http://mxcl.dev/#donate)
|
||||
[Sponsor @mxcl].
|
||||
|
||||
# Handbook
|
||||
|
||||
@@ -367,7 +364,7 @@ no filesystem entry there at all check if `type` is `nil`.
|
||||
|
||||
Changing directory is dangerous, you should *always* try to avoid it and thus
|
||||
we don’t even provide the method. If you are executing a sub-process then
|
||||
use `Process.currentDirectoryURL` to change *its* working directory when it
|
||||
use `Process.currentDirectoryURL` to change *its* working directory when it
|
||||
executes.
|
||||
|
||||
If you must change directory then use `FileManager.changeCurrentDirectory` as
|
||||
@@ -408,7 +405,15 @@ developers) it is snappy and familiar.
|
||||
SwiftPM:
|
||||
|
||||
```swift
|
||||
package.append(.package(url: "https://github.com/mxcl/Path.swift.git", from: "1.0.0"))
|
||||
package.append(
|
||||
.package(url: "https://github.com/mxcl/Path.swift.git", from: "1.0.0")
|
||||
)
|
||||
|
||||
package.targets.append(
|
||||
.target(name: "Foo", dependencies: [
|
||||
.product(name: "Path", package: "Path.swift")
|
||||
])
|
||||
)
|
||||
```
|
||||
|
||||
CocoaPods:
|
||||
@@ -430,7 +435,7 @@ Carthage:
|
||||
|
||||
|
||||
[badge-platforms]: https://img.shields.io/badge/platforms-macOS%20%7C%20Linux%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS-lightgrey.svg
|
||||
[badge-languages]: https://img.shields.io/badge/swift-4.2%20%7C%205.0%20%7C%205.1%20%7C%205.2%20%7C%205.3-orange.svg
|
||||
[badge-languages]: https://img.shields.io/badge/swift-4.2%20%7C%205.x-orange.svg
|
||||
[docs]: https://mxcl.dev/Path.swift/Structs/Path.html
|
||||
[badge-jazzy]: https://raw.githubusercontent.com/mxcl/Path.swift/gh-pages/badge.svg?sanitize=true
|
||||
[badge-codecov]: https://codecov.io/gh/mxcl/Path.swift/branch/master/graph/badge.svg
|
||||
@@ -439,3 +444,4 @@ Carthage:
|
||||
[codecov]: https://codecov.io/gh/mxcl/Path.swift
|
||||
[badge-version]: https://img.shields.io/cocoapods/v/Path.swift.svg?label=version
|
||||
[cocoapods]: https://cocoapods.org/pods/Path.swift
|
||||
[Sponsor @mxcl]: https://github.com/sponsors/mxcl
|
||||
@@ -14,6 +14,18 @@ extension Path {
|
||||
return .init(string: "/")
|
||||
}
|
||||
|
||||
#if swift(>=5.3)
|
||||
public static func source(for filePath: String = #filePath) -> (file: DynamicPath, directory: DynamicPath) {
|
||||
let file = DynamicPath(string: filePath)
|
||||
return (file: file, directory: .init(file.parent))
|
||||
}
|
||||
#else
|
||||
public static func source(for filePath: String = #file) -> (file: DynamicPath, directory: DynamicPath) {
|
||||
let file = DynamicPath(string: filePath)
|
||||
return (file: file, directory: .init(file.parent))
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Returns a `Path` representing the user’s home directory
|
||||
public static var home: DynamicPath {
|
||||
let string: String
|
||||
|
||||
@@ -29,7 +29,8 @@ public extension Pathish {
|
||||
if overwrite, let tokind = to.type, tokind != .directory, type != .directory {
|
||||
try FileManager.default.removeItem(at: to.url)
|
||||
}
|
||||
#if os(Linux) && !swift(>=5.3) // check if fixed
|
||||
#if os(Linux)
|
||||
//NOTE doing manually due to inconsistency in Linux Foundation behavior
|
||||
if !overwrite, to.type != nil {
|
||||
throw CocoaError.error(.fileWriteFileExists)
|
||||
}
|
||||
@@ -69,7 +70,8 @@ public extension Pathish {
|
||||
if overwrite, let kind = rv.type, kind != .directory {
|
||||
try FileManager.default.removeItem(at: rv.url)
|
||||
}
|
||||
#if os(Linux) && !swift(>=5.3) // check if fixed
|
||||
#if os(Linux)
|
||||
//NOTE doing manually due to inconsistency in Linux Foundation behavior
|
||||
if !overwrite, rv.type != nil {
|
||||
throw CocoaError.error(.fileWriteFileExists)
|
||||
}
|
||||
|
||||
@@ -168,6 +168,7 @@ public extension Pathish {
|
||||
*/
|
||||
var parent: Path {
|
||||
let index = string.lastIndex(of: "/")!
|
||||
guard index != string.indices.startIndex else { return Path(string: "/") }
|
||||
let substr = string[string.indices.startIndex..<index]
|
||||
return Path(string: String(substr))
|
||||
}
|
||||
|
||||
@@ -192,6 +192,14 @@ class PathTests: XCTestCase {
|
||||
XCTAssertEqual(Path.root/"a/foo"/"../../../bar", Path.root/"bar")
|
||||
}
|
||||
|
||||
func testParent() {
|
||||
XCTAssertEqual(Path("/root/boot")!.parent.string, "/root")
|
||||
XCTAssertEqual(Path("/root/boot")!.parent.parent.string, "/")
|
||||
XCTAssertEqual(Path("/root/boot")!.parent.parent.parent.string, "/")
|
||||
XCTAssertEqual(Path("/root")!.parent.string, "/")
|
||||
XCTAssertEqual(Path("/root")!.parent.parent.string, "/")
|
||||
}
|
||||
|
||||
func testDynamicMember() {
|
||||
XCTAssertEqual(Path.root.Documents, Path.root/"Documents")
|
||||
|
||||
@@ -199,7 +207,7 @@ class PathTests: XCTestCase {
|
||||
XCTAssertEqual(a.Documents, Path.home/"foo/Documents")
|
||||
|
||||
// verify use of the dynamic-member-subscript works according to our rules
|
||||
XCTAssertEqual(Path.home[dynamicMember: "../~foo"].string, "\(Path.home.parent.string)/~foo")
|
||||
XCTAssertEqual(Path.home[dynamicMember: "../~foo"].string, Path(Path.home).parent.join("~foo").string)
|
||||
}
|
||||
|
||||
func testCopyTo() throws {
|
||||
@@ -305,6 +313,13 @@ class PathTests: XCTestCase {
|
||||
XCTAssertEqual(Path.root.string, "/")
|
||||
XCTAssertEqual(Path.home.string, NSHomeDirectory())
|
||||
XCTAssertEqual(Path.documents.string, NSHomeDirectory() + "/Documents")
|
||||
#if swift(>=5.3)
|
||||
let filePath = Path(#filePath)!
|
||||
#else
|
||||
let filePath = Path(#file)!
|
||||
#endif
|
||||
XCTAssertEqual(Path.source().file, filePath)
|
||||
XCTAssertEqual(Path.source().directory, filePath.parent)
|
||||
#if !os(Linux)
|
||||
XCTAssertEqual(Path.caches.string, NSHomeDirectory() + "/Library/Caches")
|
||||
XCTAssertEqual(Path.cwd.string, FileManager.default.currentDirectoryPath)
|
||||
@@ -373,17 +388,13 @@ class PathTests: XCTestCase {
|
||||
|
||||
func testTimes() throws {
|
||||
try Path.mktemp { tmpdir in
|
||||
let foo = try tmpdir.foo.touch()
|
||||
let now1 = Date().timeIntervalSince1970.rounded(.down)
|
||||
#if !os(Linux)
|
||||
XCTAssertEqual(foo.ctime?.timeIntervalSince1970.rounded(.down), now1) //FIXME flakey
|
||||
#endif
|
||||
XCTAssertEqual(foo.mtime?.timeIntervalSince1970.rounded(.down), now1) //FIXME flakey
|
||||
sleep(1)
|
||||
try foo.touch()
|
||||
let now2 = Date().timeIntervalSince1970.rounded(.down)
|
||||
XCTAssertNotEqual(now1, now2)
|
||||
XCTAssertEqual(foo.mtime?.timeIntervalSince1970.rounded(.down), now2) //FIXME flakey
|
||||
let foo = try tmpdir.foo.touch()
|
||||
#if !os(Linux)
|
||||
XCTAssertGreaterThan(foo.ctime?.timeIntervalSince1970.rounded(.down) ?? 0, now1) //FIXME flakey
|
||||
#endif
|
||||
XCTAssertGreaterThan(foo.mtime?.timeIntervalSince1970.rounded(.down) ?? 0, now1) //FIXME flakey
|
||||
|
||||
XCTAssertNil(tmpdir.void.mtime)
|
||||
XCTAssertNil(tmpdir.void.ctime)
|
||||
@@ -447,7 +458,7 @@ class PathTests: XCTestCase {
|
||||
#if os(macOS)
|
||||
XCTAssertEqual(bndl.defaultSharedFrameworksPath, tmpdir.Contents.Frameworks)
|
||||
XCTAssertEqual(bndl.defaultResourcesPath, tmpdir.Contents.Resources)
|
||||
#elseif os(tvOS) || os(iOS)
|
||||
#elseif os(tvOS) || os(iOS) || os(watchOS)
|
||||
XCTAssertEqual(bndl.defaultSharedFrameworksPath, tmpdir.Frameworks)
|
||||
XCTAssertEqual(bndl.defaultResourcesPath, tmpdir)
|
||||
#else
|
||||
@@ -499,8 +510,19 @@ class PathTests: XCTestCase {
|
||||
|
||||
func testTouchThrowsIfCannotWrite() throws {
|
||||
try Path.mktemp { tmpdir in
|
||||
|
||||
print(try FileManager.default.attributesOfItem(atPath: tmpdir.string)[.posixPermissions])
|
||||
|
||||
//FIXME fails in Docker image (only)
|
||||
try tmpdir.chmod(0o000)
|
||||
|
||||
let attrs = try FileManager.default.attributesOfItem(atPath: tmpdir.string)
|
||||
XCTAssertEqual(attrs[.posixPermissions] as? Int, 0)
|
||||
|
||||
print(attrs[.posixPermissions])
|
||||
|
||||
XCTAssertThrowsError(try tmpdir.bar.touch())
|
||||
XCTAssertFalse(tmpdir.bar.exists)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,11 +682,3 @@ class PathTests: XCTestCase {
|
||||
XCTAssertEqual(Path("/foo"), Path.root.foo)
|
||||
}
|
||||
}
|
||||
|
||||
private func XCTAssertEqual<P: Pathish, Q: Pathish>(_ p: P, _ q: Q, file: StaticString = #file, line: UInt = #line) {
|
||||
XCTAssertEqual(p.string, q.string, file: file, line: line)
|
||||
}
|
||||
|
||||
private func XCTAssertEqual<P: Pathish, Q: Pathish>(_ p: P?, _ q: Q?, file: StaticString = #file, line: UInt = #line) {
|
||||
XCTAssertEqual(p?.string, q?.string, file: file, line: line)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ extension PathTests {
|
||||
("testMoveTo", testMoveTo),
|
||||
("testNoUndesiredSymlinkResolution", testNoUndesiredSymlinkResolution),
|
||||
("testOptionalInitializer", testOptionalInitializer),
|
||||
("testParent", testParent),
|
||||
("testPathComponents", testPathComponents),
|
||||
("testReadlinkOnFileReturnsSelf", testReadlinkOnFileReturnsSelf),
|
||||
("testReadlinkOnNonExistantFileThrows", testReadlinkOnNonExistantFileThrows),
|
||||
|
||||
@@ -1,11 +1,43 @@
|
||||
import XCTest
|
||||
import Path
|
||||
|
||||
#if swift(>=5.3)
|
||||
func XCTAssertEqual<P: Pathish>(_ set1: Set<Path>, _ set2: Set<Path>, _ message: @autoclosure () -> String = "", file: StaticString = #filePath, line: UInt = #line, relativeTo: P) {
|
||||
logic(set1, set2, relativeTo: relativeTo) {
|
||||
XCTFail($0, file: file, line: line)
|
||||
}
|
||||
}
|
||||
#else
|
||||
func XCTAssertEqual<P: Pathish>(_ set1: Set<Path>, _ set2: Set<Path>, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line, relativeTo: P) {
|
||||
logic(set1, set2, relativeTo: relativeTo) {
|
||||
XCTFail($0, file: file, line: line)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private func logic<P: Pathish>(_ set1: Set<Path>, _ set2: Set<Path>, relativeTo: P, fail: (String) -> Void) {
|
||||
if set1 != set2 {
|
||||
let cvt: (Path) -> String = { $0.relative(to: relativeTo) }
|
||||
let out1 = set1.map(cvt).sorted()
|
||||
let out2 = set1.map(cvt).sorted()
|
||||
XCTFail("Set(\(out1)) is not equal to Set(\(out2))", file: file, line: line)
|
||||
fail("Set(\(out1)) is not equal to Set(\(out2))")
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.3)
|
||||
func XCTAssertEqual<P: Pathish, Q: Pathish>(_ p: P, _ q: Q, file: StaticString = #filePath, line: UInt = #line) {
|
||||
XCTAssertEqual(p.string, q.string, file: file, line: line)
|
||||
}
|
||||
|
||||
func XCTAssertEqual<P: Pathish, Q: Pathish>(_ p: P?, _ q: Q?, file: StaticString = #filePath, line: UInt = #line) {
|
||||
XCTAssertEqual(p?.string, q?.string, file: file, line: line)
|
||||
}
|
||||
#else
|
||||
func XCTAssertEqual<P: Pathish, Q: Pathish>(_ p: P, _ q: Q, file: StaticString = #file, line: UInt = #line) {
|
||||
XCTAssertEqual(p.string, q.string, file: file, line: line)
|
||||
}
|
||||
|
||||
func XCTAssertEqual<P: Pathish, Q: Pathish>(_ p: P?, _ q: Q?, file: StaticString = #file, line: UInt = #line) {
|
||||
XCTAssertEqual(p?.string, q?.string, file: file, line: line)
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user