Compare commits

...

23 Commits

Author SHA1 Message Date
repo-ranger[bot]
e915bc0cfb Merge pull request #43 from mxcl/Bundle.executable
Add Bundle.executable
2019-02-17 15:14:17 +00:00
Max Howell
f4c2c75aa1 Add Bundle.executable 2019-02-17 10:05:30 -05:00
repo-ranger[bot]
dc7affa28c Merge pull request #42 from mxcl/path-components
Add Path.components
2019-02-15 18:49:13 +00:00
Max Howell
476cdc1461 Add Path.components 2019-02-15 13:38:36 -05:00
Max Howell
a644208c62 Test Swift 4.0.3 also 2019-02-13 21:34:35 -05:00
Max Howell
d7a9819350 Fix publishing the release in the deploy stage
[ci skip]
2019-02-13 20:52:39 -05:00
repo-ranger[bot]
24a54c2ee0 Merge pull request #39 from mxcl/less-manifests-test
You can specify future Swifts in a 4.2 manifest!
2019-02-14 01:00:02 +00:00
Max Howell
3735ed4476 You can specify future Swifts in a 4.2 manifest! 2019-02-13 19:40:07 -05:00
Max Howell
2880aa556b This hack did not work in fact 2019-02-13 16:28:16 -05:00
repo-ranger[bot]
a125a871f5 Merge pull request #38 from mxcl/tweaks
Tweaks
2019-02-13 21:13:29 +00:00
Max Howell
d79844cf2b Use a symlink to prevent Package.swift divergence 2019-02-13 15:53:41 -05:00
Max Howell
d0648411ea Get minimum supported Swift version for CocoaPods 2019-02-13 15:47:40 -05:00
Max Howell
e74cc63271 [ci skip] Fix publish release command 2019-02-13 15:27:06 -05:00
Max Howell
28f84d3961 Deploy needs Swift 5 2019-02-13 10:54:10 -05:00
repo-ranger[bot]
db184a13a3 Merge pull request #37 from mxcl/deploy-script
“Scriptify” deployment
2019-02-13 15:28:21 +00:00
Max Howell
b65d167937 “Scriptify” deployment 2019-02-13 10:12:32 -05:00
repo-ranger[bot]
9a770ca576 Merge pull request #36 from mxcl/remove-deployment-targets
These deployment versions are the defaults
2019-02-13 14:49:00 +00:00
Max Howell
b7c189e6af These deployment versions are the defaults
Well, almost, but adjusted we still work so in fact the ”bump” was spurious.
2019-02-13 09:32:19 -05:00
repo-ranger[bot]
2758f0f698 Merge pull request #35 from mxcl/ci-xcode10.2
[ci] Xcode 10.2
2019-02-12 20:41:53 +00:00
Max Howell
e68ad25cc0 [ci] Xcode 10.2 2019-02-12 15:23:27 -05:00
Max Howell
c9d300a7b6 Path(_ url:) -> Path(url:) 2019-02-11 20:40:27 -05:00
Max Howell
ed4b773870 Fill in this TODO in README
[skip ci]
2019-02-11 15:13:14 -05:00
Max Howell
097e020735 There are no usernames on iOS etc. 2019-02-11 15:11:22 -05:00
10 changed files with 246 additions and 101 deletions

168
.github/deploy vendored Executable file
View File

@@ -0,0 +1,168 @@
#!/usr/bin/swift sh
import func Darwin.fputs
import var Darwin.stderr
import PMKFoundation // PromiseKit/Foundation ~> 3.3
import LegibleError // @mxcl ~> 1.0
import Foundation
import PromiseKit // @mxcl ~> 6.8
import Path // mxcl/Path.swift ~> 0.15
let env = ProcessInfo.processInfo.environment
let token = env["GITHUB_TOKEN"] ?? env["GITHUB_ACCESS_TOKEN"]!
let slug = env["TRAVIS_REPO_SLUG"]!
let tag = env["TRAVIS_TAG"]!
func fatal(message: String) -> Never {
fputs("error: \(message)\n", stderr)
exit(1)
}
func fatal(error: Error) -> Never {
fatal(message: "\(error.legibleLocalizedDescription)\n\n\(error.legibleDescription)")
}
struct Repo: Decodable {
let description: String
let license: License
struct License: Decodable {
let spdx_id: String
}
}
struct Package: Decodable {
let swiftLanguageVersions: [String]
let targets: [Target]
struct Target: Decodable {
let path: String?
let type: Kind
enum Kind: String, Decodable {
case regular
case test
}
}
}
extension URLRequest {
init(github path: String) {
let url = URL(string: "https://api.github.com\(path)")!
self.init(url: url)
setValue("token \(token)", forHTTPHeaderField: "Authorization")
setValue("application/json", forHTTPHeaderField: "Content-Type")
setValue("application/json", forHTTPHeaderField: "Accept")
}
}
func description() -> Promise<Repo> {
let rq = URLRequest(github: "/repos/\(slug)")
return firstly {
URLSession.shared.dataTask(.promise, with: rq).validate()
}.map { data, _ in
try JSONDecoder().decode(Repo.self, from: data)
}
}
struct User: Decodable {
let name: String
let email: String
}
func email() -> Promise<User> {
let rq = URLRequest(github: "/user")
return firstly {
URLSession.shared.dataTask(.promise, with: rq).validate()
}.map { data, _ in
try JSONDecoder().decode(User.self, from: data)
}
}
func dumpPackage() -> Promise<Package> {
let task = Process()
task.launchPath = "/usr/bin/swift"
task.arguments = ["package", "dump-package"]
return firstly {
task.launch(.promise)
}.map { out, _ in
out.fileHandleForReading.readDataToEndOfFile()
}.map { data in
try JSONDecoder().decode(Package.self, from: data)
}
}
var defaultSwiftVersion: String {
let task = Process()
task.launchPath = "/usr/bin/swift"
task.arguments = ["--version"]
func extract(input: String) -> String {
let range = input.range(of: #"Apple Swift version \d+\.\d+"#, options: .regularExpression)!
return String(input[range].split(separator: " ").last!)
}
return try! firstly {
task.launch(.promise)
}.compactMap { out, _ in
String(data: out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
}.map { out in
extract(input: out)
}.wait()
}
func podspec(repo: Repo, user: User, pkg: Package) -> (Substring, String) {
let (owner, name) = { ($0[0], $0[1]) }(slug.split(separator: "/"))
let swiftVersion = pkg.swiftLanguageVersions.min() ?? defaultSwiftVersion
let targets = pkg.targets.filter{ $0.type == .regular }
guard targets.count == 1 else { fatal(message: "Too many targets for this script!") }
guard let sources = targets[0].path else { fatal(message: "Target has no path!") }
return (name, """
Pod::Spec.new do |s|
s.name = '\(name)'
s.version = '\(tag)'
s.summary = '\(repo.description)'
s.homepage = "https://github.com/\(slug)"
s.license = '\(repo.license.spdx_id)'
s.author = { '\(user.name)': '\(user.email)' }
s.source = { git: "https://github.com/\(slug).git", tag: '\(tag)' }
s.social_media_url = 'https://twitter.com/\(owner)'
s.osx.deployment_target = '10.10'
s.ios.deployment_target = '8.0'
s.tvos.deployment_target = '9.0'
s.watchos.deployment_target = '2.0'
s.source_files = '\(sources)/*.swift'
s.swift_version = '\(swiftVersion)'
end
""")
}
func publishRelease() throws -> Promise<Void> {
struct Input: Encodable {
let tag_name = tag
let name = tag
let body = ""
}
var rq = URLRequest(github: "/repos/\(slug)/releases")
rq.httpMethod = "POST"
rq.httpBody = try JSONEncoder().encode(Input())
return URLSession.shared.dataTask(.promise, with: rq).validate().asVoid()
}
switch CommandLine.arguments[1] {
case "generate-podspec":
firstly {
when(fulfilled: description(), email(), dumpPackage())
}.map(podspec).done { name, podspec in
try podspec.write(toFile: "\(name).podspec", atomically: false, encoding: .utf8)
exit(0)
}.catch {
fatal(error: $0)
}
case "publish-release":
try publishRelease().done {
exit(0)
}.catch {
fatal(error: $0)
}
default:
fatal(message: "invalid usage")
}
RunLoop.main.run()

13
.github/jazzy.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
module: Path
custom_categories:
- name: Path
children:
- Path
- /(_:_:)
xcodebuild_arguments:
- UseModernBuildSystem=NO
output:
../output
# output directory is relative to config file… ugh
exclude:
- Sources/Path+StringConvertibles.swift

View File

@@ -12,20 +12,29 @@ language: swift
osx_image: xcode10.1
xcode_project: Path.swift.xcodeproj
xcode_scheme: Path.swift-Package
jobs:
include:
- script: swift test --parallel
name: macOS / Swift 4.2.1
- name: macOS / Swift 4.0.3
script: swift test --parallel -Xswiftc -swift-version -Xswiftc 4
- name: macOS / Swift 4.2.1
script: swift test --parallel
- name: macOS / Swift 5.0
osx_image: xcode10.2
script: swift test --parallel
- &xcodebuild
before_install: swift package generate-xcodeproj --enable-code-coverage
xcode_destination: platform=iOS Simulator,OS=latest,name=iPhone XS
name: iOS / Swift 4.2.1
after_success: bash <(curl -s https://codecov.io/bash)
- <<: *xcodebuild
xcode_destination: platform=tvOS Simulator,OS=latest,name=Apple TV
name: tvOS / Swift 4.2.1
- <<: *xcodebuild
name: watchOS / Swift 4.2.1
script: |
@@ -46,11 +55,11 @@ jobs:
sudo: false
install: eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
script: swift test --parallel
- <<: *linux
env: SWIFT_VERSION='5.0-DEVELOPMENT-SNAPSHOT-2019-01-22-a'
name: Linux / Swift 5.0.0-dev (2019-01-22)
name: Linux / Swift 5.0.0-dev+2019.01.22
- stage: pretest
name: Check Linux tests are syncd
install: swift test --generate-linuxmain
@@ -58,29 +67,12 @@ jobs:
- stage: deploy
name: Jazzy
before_install: |
cat <<\ \ EOF> .jazzy.yaml
module: Path
module_version: TRAVIS_TAG
custom_categories:
- name: Path
children:
- Path
- /(_:_:)
xcodebuild_arguments:
- UseModernBuildSystem=NO
output: output
github_url: https://github.com/mxcl/Path.swift
exclude:
- Sources/Path+StringConvertibles.swift
EOF
sed -i '' "s/TRAVIS_TAG/$TRAVIS_TAG/" .jazzy.yaml
# ^^ this weirdness because Travis multiline YAML is broken and inserts
# two spaces in front of the output which means we need a prefixed
# delimiter which also weirdly stops bash from doing variable substitution
install: gem install jazzy
before_script: swift package generate-xcodeproj
script: jazzy
script: |
jazzy --config .github/jazzy.yml \
--module-version $TRAVIS_TAG \
--github_url "https://github.com/$TRAVIS_REPO_SLUG"
deploy:
provider: pages
skip-cleanup: true
@@ -90,43 +82,8 @@ jobs:
tags: true
- name: CocoaPods
before_install: export TRAVIS_REPO_NAME=${TRAVIS_REPO_SLUG#*/}
install: gem install cocoapods
before_script: |
export DESCRIPTION=$(swift - <<\ \ EOF
import Foundation
struct Response: Decodable { let description: String }
let token = ProcessInfo.processInfo.environment["GITHUB_TOKEN"]!
let slug = ProcessInfo.processInfo.environment["TRAVIS_REPO_SLUG"]!
let url = URL(string: "https://api.github.com/repos/\(slug)")!
var rq = URLRequest(url: url)
rq.setValue("token \(token)", forHTTPHeaderField: "Authorization")
let semaphore = DispatchSemaphore(value: 0)
var data: Data!
URLSession.shared.dataTask(with: rq) { d, _, _ in
data = d
semaphore.signal()
}.resume()
semaphore.wait()
let rsp = try JSONDecoder().decode(Response.self, from: data)
print(rsp.description, terminator: "")
EOF)
cat <<\ \ EOF> $TRAVIS_REPO_NAME.podspec
Pod::Spec.new do |s|
s.name = ENV['TRAVIS_REPO_NAME']
s.version = ENV['TRAVIS_TAG']
s.summary = ENV['DESCRIPTION']
s.homepage = "https://github.com/#{ENV['TRAVIS_REPO_SLUG']}"
s.license = { type: 'Unlicense', file: 'LICENSE.md' }
s.author = { mxcl: 'mxcl@me.com' }
s.source = { git: "https://github.com/#{ENV['TRAVIS_REPO_SLUG']}.git", tag: s.version }
s.social_media_url = 'https://twitter.com/mxcl'
s.osx.deployment_target = '10.10'
s.ios.deployment_target = '8.0'
s.tvos.deployment_target = '10.0'
s.watchos.deployment_target = '3.0'
s.source_files = 'Sources/*'
s.swift_version = '4.2'
end
EOF
osx_image: xcode10.2
install: brew install mxcl/made/swift-sh
before_script: .github/deploy generate-podspec
script: pod trunk push
after_success: .github/deploy publish-release

View File

@@ -1,7 +1,7 @@
// swift-tools-version:4.2
import PackageDescription
let package = Package(
let pkg = Package(
name: "Path.swift",
products: [
.library(name: "Path", targets: ["Path"]),
@@ -9,5 +9,6 @@ let package = Package(
targets: [
.target(name: "Path", path: "Sources"),
.testTarget(name: "PathTests", dependencies: ["Path"]),
]
],
swiftLanguageVersions: [.v4, .v4_2, .version("5")]
)

View File

@@ -1,20 +0,0 @@
// swift-tools-version:5.0
import PackageDescription
let pkg = Package(
name: "Path.swift",
products: [
.library(name: "Path", targets: ["Path"]),
],
targets: [
.target(name: "Path", path: "Sources"),
.testTarget(name: "PathTests", dependencies: ["Path"]),
]
)
pkg.platforms = [
.macOS(.v10_10), .iOS(.v8), .tvOS(.v10), .watchOS(.v3)
]
pkg.swiftLanguageVersions = [
.v4_2, .v5
]

View File

@@ -181,7 +181,7 @@ with your work without worries.
There is also some magic going on in Foundations filesystem APIs, which we look
for and ensure our API is deterministic, eg. [this test].
[this test]: TODO
[this test]: https://github.com/mxcl/Path.swift/blob/master/Tests/PathTests/PathTests.swift#L539-L554
# `Path.swift` is properly cross-platform
@@ -281,7 +281,7 @@ Therefore, if you are not using this feature you are fine. If you have URLs the
way to get a `Path` is:
```swift
if let path = Path(url) {
if let path = Path(url: url) {
/**/
}
```

View File

@@ -34,6 +34,11 @@ public extension Bundle {
var path: Path {
return Path(string: bundlePath)
}
/// Returns the executable for this bundle, if there is one, not all bundles have one hence `Optional`.
var executable: Path? {
return executablePath.flatMap(Path.init)
}
}
/// Extensions on `String` that work with `Path` rather than `String` or `URL`

View File

@@ -78,11 +78,7 @@ public struct Path: Equatable, Hashable, Comparable {
tilded = dir
}
#else
if username != NSUserName() {
return nil
} else {
tilded = NSHomeDirectory()
}
return nil // there are no usernames on iOS, etc.
#endif
}
pathComponents.remove(at: 0)
@@ -108,13 +104,22 @@ public struct Path: Equatable, Hashable, Comparable {
self.string = join_(prefix: "/", pathComponents: pathComponents)
}
public init?(_ url: URL) {
/**
Creates a new absolute, standardized path from the provided file-scheme URL.
- Note: If the URL is not a file URL, returns `nil`.
*/
public init?(url: URL) {
guard url.scheme == "file" else { return nil }
self.init(string: url.path)
self.init(url.path)
//NOTE: URL cannot be a file-reference url, unlike NSURL, so this always works
}
public init?(_ url: NSURL) {
/**
Creates a new absolute, standardized path from the provided file-scheme URL.
- Note: If the URL is not a file URL, returns `nil`.
- Note: If the URL is a file reference URL, converts it to a POSIX path first.
*/
public init?(url: NSURL) {
guard url.scheme == "file", let path = url.path else { return nil }
self.init(string: path)
// ^^ works even if the url is a file-reference url
@@ -195,6 +200,15 @@ public struct Path: Equatable, Hashable, Comparable {
}
}
/**
Splits the string representation on the directory separator.
- Important: The first element is always "/" to be consistent with `NSString.pathComponents`.
*/
@inlinable
public var components: [String] {
return ["/"] + string.split(separator: "/").map(String.init)
}
//MARK: Pathing
/**

View File

@@ -403,6 +403,7 @@ class PathTests: XCTestCase {
XCTAssertEqual(bndl.privateFrameworks, tmpdir.Frameworks)
XCTAssertEqual(bndl.resources, tmpdir)
XCTAssertNil(bndl.path(forResource: "foo", ofType: "bar"))
XCTAssertNil(bndl.executable)
#if os(macOS)
XCTAssertEqual(bndl.defaultSharedFrameworksPath, tmpdir.Contents.Frameworks)
@@ -577,10 +578,10 @@ class PathTests: XCTestCase {
}
func testURLInitializer() throws {
XCTAssertEqual(Path(Path.home.url), Path.home)
XCTAssertEqual(Path(url: Path.home.url), Path.home)
XCTAssertEqual(Path.home.fileReferenceURL.flatMap(Path.init), Path.home)
XCTAssertNil(Path(URL(string: "https://foo.com")!))
XCTAssertNil(Path(NSURL(string: "https://foo.com")!))
XCTAssertNil(Path(url: URL(string: "https://foo.com")!))
XCTAssertNil(Path(url: NSURL(string: "https://foo.com")!))
}
func testInitializerForRelativePath() throws {
@@ -588,4 +589,9 @@ class PathTests: XCTestCase {
XCTAssertNil(Path("../foo"))
XCTAssertNil(Path("./foo"))
}
func testPathComponents() throws {
XCTAssertEqual(Path.root.foo.bar.components, ["/", "foo", "bar"])
XCTAssertEqual(Path.root.components, ["/"])
}
}

View File

@@ -28,6 +28,7 @@ extension PathTests {
("testMoveInto", testMoveInto),
("testMoveTo", testMoveTo),
("testNoUndesiredSymlinkResolution", testNoUndesiredSymlinkResolution),
("testPathComponents", testPathComponents),
("testReadlinkOnFileReturnsSelf", testReadlinkOnFileReturnsSelf),
("testReadlinkOnNonExistantFileThrows", testReadlinkOnNonExistantFileThrows),
("testReadlinkOnRelativeSymlink", testReadlinkOnRelativeSymlink),