Skip to content

Commit

Permalink
Add asset download command to improve supporting source file setup (#101
Browse files Browse the repository at this point in the history
)

Usage: `$ mockingbird download <asset>`

- `starter-pack` Starter supporting source files

The command will download an asset bundle from the current versioned
release and unarchive the `.zip` bundle. Bundles are safely merged with
existing files on disk, such that files will never be overwritten.
  • Loading branch information
andrewchang-bird authored May 1, 2020
1 parent 986d155 commit 0ffb6a8
Show file tree
Hide file tree
Showing 15 changed files with 3,990 additions and 3,664 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/build-framework-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
--source MockingbirdTestsHost \
--loglevel verbose \
--verbose
- name: Download Starter Pack
run: ./bin/mockingbird download starter-pack
- name: Test
run: make clean-test
- name: Cached Test
Expand Down Expand Up @@ -60,6 +62,8 @@ jobs:
--source MockingbirdTestsHost \
--loglevel verbose \
--verbose
- name: Download Starter Pack
run: ./bin/mockingbird download starter-pack
- name: Test
run: make clean-test
- name: Cached Test
Expand Down Expand Up @@ -92,6 +96,8 @@ jobs:
--source MockingbirdTestsHost \
--loglevel verbose \
--verbose
- name: Download Starter Pack
run: ./bin/mockingbird download starter-pack
- name: Test
run: make clean-test
- name: Cached Test
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
build/
DerivedData
Mockingbird.pkg
mockingbird.zip
Mockingbird.zip
MockingbirdCli.pkg
mockingbirdCli.zip
MockingbirdCli.zip
MockingbirdSupport.zip
mockingbird

## Caches
Expand Down
10 changes: 9 additions & 1 deletion Examples/iOSMockingbirdExample-Carthage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ $ unzip -o 'MockingbirdSupport.zip'
$ rm -f 'MockingbirdSupport.zip'
```

<details><summary>Upcoming changes in Mockingbird 0.12.0</summary>

```bash
$ mockingbird download starter-pack
```

</details>

### Run Tests

Open the Xcode project.
Expand All @@ -75,7 +83,7 @@ Open the Xcode project.
$ open iOSMockingbirdExample-Carthage.xcodeproj
```

Take a peek at the example test and sources and then run the tests (⌘+U):
Take a peek at the example test and sources and then run the tests (⌘+U).

- [`TreeTests.swift`](iOSMockingbirdExample-CarthageTests/TreeTests.swift)
- [`Tree.swift`](iOSMockingbirdExample-Carthage/Tree.swift)
Expand Down
10 changes: 9 additions & 1 deletion Examples/iOSMockingbirdExample-CocoaPods/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ $ unzip -o 'MockingbirdSupport.zip'
$ rm -f 'MockingbirdSupport.zip'
```

<details><summary>Upcoming changes in Mockingbird 0.12.0</summary>

```bash
$ mockingbird download starter-pack
```

</details>

### Run Tests

Open the Xcode workspace generated by CocoaPods.
Expand All @@ -67,7 +75,7 @@ Open the Xcode workspace generated by CocoaPods.
$ open iOSMockingbirdExample-CocoaPods.xcworkspace
```

Take a peek at the example test and sources and then run the tests (⌘+U):
Take a peek at the example test and sources and then run the tests (⌘+U).

- [`TreeTests.swift`](iOSMockingbirdExample-CocoaPodsTests/TreeTests.swift)
- [`Tree.swift`](iOSMockingbirdExample-CocoaPods/Tree.swift)
Expand Down
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ INSTALLABLE_FILENAMES="$(CLI_FILENAME)" \
"$(APPLETVSIMULATOR_FRAMEWORK_FILENAME)" \
"$(LICENSE_FILENAME)"

STARTER_PACK_FOLDER=MockingbirdSupport

OUTPUT_PACKAGE=Mockingbird.pkg
OUTPUT_ZIP=Mockingbird.zip
OUTPUT_STARTER_PACK_ZIP=MockingbirdSupport.zip

CLI_BUNDLE_PLIST=MockingbirdCli/Info.plist
VERSION_STRING=$(shell /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$(CLI_BUNDLE_PLIST)")
Expand Down Expand Up @@ -117,6 +120,7 @@ ERROR_MSG=[ERROR] The downloaded Mockingbird CLI binary does not have the expect
prepare-zip \
zip \
signed-zip \
starter-pack-zip \
release \
signed-release \
get-version \
Expand Down Expand Up @@ -305,9 +309,12 @@ signed-zip: installables prepare-zip

(cd "$(TEMPORARY_INSTALLER_FOLDER)"; zip -yr - $(INSTALLABLE_FILENAMES)) > "$(OUTPUT_ZIP)"

release: clean package zip
starter-pack-zip:
zip -yr - $(STARTER_PACK_FOLDER) > "$(OUTPUT_STARTER_PACK_ZIP)"

release: clean package zip starter-pack-zip

signed-release: clean signed-package signed-zip
signed-release: clean signed-package signed-zip starter-pack-zip

get-version:
@echo $(VERSION_STRING)
Expand Down
25 changes: 25 additions & 0 deletions Mockingbird.xcodeproj/ZIPFoundation_Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
7,308 changes: 3,693 additions & 3,615 deletions Mockingbird.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
9 changes: 9 additions & 0 deletions MockingbirdCli/Interface/ArgumentParser+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ extension ArgumentParser {
kind: Bool.self,
usage: "Only search explicitly imported modules.")
}

// MARK: - Positional

func addAssetBundleType() -> PositionalArgument<AssetBundleType> {
return add(positional: "asset",
kind: AssetBundleType.self,
usage: "An asset bundle to download and unpack.",
completion: AssetBundleType.completion)
}
}

extension ArgumentParser.Result {
Expand Down
142 changes: 142 additions & 0 deletions MockingbirdCli/Interface/Commands/DownloadCommand.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//
// DownloadCommand.swift
// MockingbirdCli
//
// Created by Andrew Chang on 4/26/20.
//

import Foundation
import MockingbirdGenerator
import PathKit
import SPMUtility
import ZIPFoundation

enum AssetBundleType: String, ArgumentKind, CaseIterable, CustomStringConvertible {
case starterPack = "starter-pack"

init(argument: String) throws {
guard AssetBundleType(rawValue: argument) != nil else {
let allOptions = AssetBundleType.allCases.map({ $0.rawValue }).joined(separator: ", ")
throw ArgumentParserError.invalidValue(
argument: "asset",
error: .custom("\(argument.singleQuoted) is not a valid download type, expected: \(allOptions)")
)
}
self.init(rawValue: argument)!
}

static var completion: ShellCompletion {
return .values(AssetBundleType.allCases.map({
(value: $0.rawValue, description: "\($0)")
}))
}

var description: String {
switch self {
case .starterPack: return "Starter supporting source files."
}
}

private func assetBundleUrl(for fileName: String) -> Foundation.URL {
return Foundation.URL(string:
"https://github.com/birdrides/mockingbird/releases/download/\(mockingbirdVersion)/\(fileName)"
)!
}
var url: Foundation.URL {
switch self {
case .starterPack: return assetBundleUrl(for: "MockingbirdSupport.zip")
}
}
}

final class DownloadCommand: BaseCommand {
private enum Constants {
static let name = "download"
static let overview = "Download and unpack a compatible asset bundle."

static let excludedAssetRootDirectories: Set<String> = [
"__MACOSX",
]
static let excludedAssetFileNames: Set<String> = [
".DS_Store",
]
}
override var name: String { return Constants.name }
override var overview: String { return Constants.overview }

private let assetBundleTypeArgument: PositionalArgument<AssetBundleType>
private let projectPathArgument: OptionArgument<PathArgument>

required init(parser: ArgumentParser) {
let subparser = parser.add(subparser: Constants.name, overview: Constants.overview)
self.assetBundleTypeArgument = subparser.addAssetBundleType()
self.projectPathArgument = subparser.addProjectPath()
super.init(parser: subparser)
}

override func run(with arguments: ArgumentParser.Result,
environment: [String: String],
workingPath: Path) throws {
let projectPath = try arguments.getProjectPath(using: projectPathArgument,
environment: environment,
workingPath: workingPath)
let inferredRootPath = projectPath.parent()

try super.run(with: arguments, environment: environment, workingPath: workingPath)
guard let type = arguments.get(assetBundleTypeArgument) else { return }

print("Downloading asset bundle from \(type.url)")
guard let fileUrl = downloadAssetBundle(type.url) else {
log("Unable to download asset bundle \(type.rawValue.singleQuoted)", type: .error)
exit(1)
}

log("Temporary asset bundle data stored at \(fileUrl)")
print("Extracting downloaded asset bundle to \(Path().absolute())")
guard let archive = Archive(url: fileUrl, accessMode: .read) else {
log("The downloaded asset bundle is corrupted", type: .error)
exit(1)
}

try self.extractAssetBundle(archive, to: inferredRootPath)
flushLogs()
print("Successfully loaded asset bundle \(type.rawValue.singleQuoted) into \(inferredRootPath)")
}

private func downloadAssetBundle(_ url: Foundation.URL) -> Foundation.URL? {
let semaphore = DispatchSemaphore(value: 0)
var fileUrl: Foundation.URL?
URLSession.shared.downloadTask(with: url) { (url, _, error) in
if let error = error { log(error) }
fileUrl = url
semaphore.signal()
}.resume()
semaphore.wait()
return fileUrl
}

private func extractAssetBundle(_ archive: Archive, to path: Path) throws {
let basePath = path.absolute()
for entry in archive {
let entryPath = Path(entry.path)
guard
let firstComponent = entryPath.components.first,
!Constants.excludedAssetRootDirectories.contains(firstComponent)
else {
log("Skipping excluded asset bundle entry based on root directory at \(entryPath)")
continue
}
guard !Constants.excludedAssetFileNames.contains(entryPath.lastComponent) else {
log("Skipping excluded asset bundle entry based on file name at \(entryPath)")
continue
}

let destinationPath = basePath + entryPath
guard !destinationPath.exists else {
logWarning("Skipping existing asset bundle contents at \(entryPath)")
continue
}
_ = try archive.extract(entry, to: destinationPath.url)
}
}
}
7 changes: 7 additions & 0 deletions MockingbirdCli/Interface/Commands/InstallCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,12 @@ final class InstallCommand: BaseCommand {
)
try Installer.install(using: config)
print("Installed Mockingbird to `\(destinationTarget)` in \(projectPath)")

// Warn users that haven't added supporting source files.
guard supportPath == nil else { return }
print("""
Please add starter supporting source files for basic compatibility with system frameworks.
$ mockingbird download starter-pack
""")
}
}
1 change: 1 addition & 0 deletions MockingbirdCli/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func main(arguments: [String]) -> Int32 {
commands: [GenerateCommand.self,
InstallCommand.self,
UninstallCommand.self,
DownloadCommand.self,
TestbedCommand.self,
VersionCommand.self])
return program.run(with: arguments)
Expand Down
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@
"revision": "c947a306d2e80ecb2c0859047b35c73b8e1ca27f",
"version": "2.0.0"
}
},
{
"package": "ZIPFoundation",
"repositoryURL": "https://github.com/weichsel/ZIPFoundation.git",
"state": {
"branch": null,
"revision": "ec32d62d412578542c0ffb7a6ce34d3e64b43b94",
"version": "0.9.11"
}
}
]
},
Expand Down
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ let package = Package(
//.library(name: "MockingbirdGenerator", targets: ["MockingbirdGenerator"]),
],
dependencies: [
.package(url: "https://github.com/tuist/XcodeProj.git", from: "7.0.0"),
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.24.0"),
.package(url: "https://github.com/apple/swift-package-manager.git", .exact("0.4.0")),
.package(url: "https://github.com/apple/swift-syntax.git", .exact("0.50200.0")),
.package(url: "https://github.com/jpsim/SourceKitten.git", from: "0.24.0"),
.package(url: "https://github.com/tuist/XcodeProj.git", from: "7.0.0"),
.package(url: "https://github.com/weichsel/ZIPFoundation.git", from: "0.9.0"),
],
targets: [
.target(
Expand All @@ -42,6 +43,7 @@ let package = Package(
"MockingbirdGenerator",
"SPMUtility",
"XcodeProj",
"ZIPFoundation",
],
path: "MockingbirdCli"
),
Expand Down
Loading

0 comments on commit 0ffb6a8

Please sign in to comment.