{"id":13880624,"url":"https://github.com/ChristianKienle/highway","last_synced_at":"2025-07-16T17:30:50.548Z","repository":{"id":141840994,"uuid":"99488761","full_name":"ChristianKienle/highway","owner":"ChristianKienle","description":"Automate development tasks using Swift.","archived":false,"fork":false,"pushed_at":"2020-03-18T14:52:01.000Z","size":701,"stargazers_count":76,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-02T23:04:54.928Z","etag":null,"topics":["automate","automation","ci","commandline","packagemanager","release","swift","testing","xcodebuild"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChristianKienle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-08-06T13:36:40.000Z","updated_at":"2025-01-20T12:52:50.000Z","dependencies_parsed_at":"2024-01-13T21:11:03.130Z","dependency_job_id":null,"html_url":"https://github.com/ChristianKienle/highway","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/ChristianKienle/highway","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChristianKienle%2Fhighway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChristianKienle%2Fhighway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChristianKienle%2Fhighway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChristianKienle%2Fhighway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChristianKienle","download_url":"https://codeload.github.com/ChristianKienle/highway/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChristianKienle%2Fhighway/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265527543,"owners_count":23782480,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automate","automation","ci","commandline","packagemanager","release","swift","testing","xcodebuild"],"created_at":"2024-08-06T08:03:18.832Z","updated_at":"2025-07-16T17:30:50.013Z","avatar_url":"https://github.com/ChristianKienle.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"Highway\n=================\n\n# Building\n\n```\nalias swiftxcode=\"swift package generate-xcodeproj --xcconfig-overrides Package.xcconfig\"\nalias swiftbuildMacOS='swift build -Xswiftc \"-target\" -Xswiftc \"x86_64-apple-macosx10.13\"'\nalias swifttest='swift test -Xswiftc \"-target\" -Xswiftc \"x86_64-apple-macosx10.13\"\n\n```\n## How to use\n\n`highway` allows you to quickly automate the build, test and release cycle of your iOS or macOS app. Since `highway` builds on technologies you already know  (Swift \u0026 the Swift Package Manager, Foundation, ...) getting started is super easy.\n\n# Cheat Sheet\n\n| Command                   | Description                                                 |\n|---------------------------|-------------------------------------------------------------|\n| `highway` **init**        | Initializes a new highway project in the current directory. |\n| `highway` **help**        | Displays available commands and options.                    |\n| `highway` **generate**    | Generates an Xcode project.                                 |\n| `highway` **bootstrap**   | Bootstraps the highway home directory.                      |\n| `highway` **clean**       | Delete build artifacts of your highway project.             |\n| `highway` **self_update** | Updates highway \u0026 the supporting frameworks                 |\n| `highway` **--version**   | Print version information and exit.                         |\n\n\n**Good to know**\n* **Unknown commands/arguments** are passed to your highway project. For example: `highway xyz` will execute your highway called `xyz`.\n* Executing `highway` **without any arguments** will print all available commands (including commands implemented by your highway project).\n* To see whats going on under the hood use `--verbose`. For example: `highway --verbose xyz`.\n\nTable of Contents\n=================\n* [Getting started](#a-namegetting-started-getting-started)\n  * [Installing highway in 60 seconds (or less)](#a-nameinstallation-installing-highway-in-60-seconds-or-less)\n  * [Setting up highway](#a-namesetup-setting-up-highway)\n  * [Hello World (the fun part)](#a-namehello-world-hello-world-the-fun-part)\n* [Make your highway project useful](#a-namemake-useful--make-your-highway-project-useful)\n  * [Creating a simple custom Highway](#creating-a-simple-custom-highway)\n  * [Highways with Dependencies](#highways-with-dependencies)\n    * [Depending on a single Highway](#depending-on-a-single-highway)\n    * [Depending on multiple Highways](#depending-on-multiple-highways)\n  * [Highways with Results](#highways-with-results)\n  * [Special Highways](#special-highways)\n* [Included Frameworks](#included-frameworks)\n  * [Swift: build \u0026 test](#swift-build--test)\n  * [Xcode: build, archive, test (...) + xcpretty](#xcode-build-archive-test---xcpretty)\n  * [Keychain: Access Secrets](#keychain-access-secrets)\n  * [git: commit, push, (auto-) tag](#git-commit-push-auto--tag)\n  * [fastlane](#fastlane)\n* [Updating](#updating)\n  * [Dependencies](#dependencies)\n  * [CLI](#cli)\n\n---\n\n# \u003ca name=\"getting-started\" /\u003eGetting started\n\n\n## \u003ca name=\"installation\" /\u003eInstalling highway in 60 seconds (or less)\n\nSimply paste the following command into a terminal of your choice.\n\n```\npushd $(mktemp -d -t highway) \u0026\u0026 \\\ngit clone -b master https://github.com/ChristianKienle/highway.git highway \u0026\u0026 \\\n./highway/scripts/bootstrap.sh --interactive \u0026\u0026 popd\n```\n\nThis will checkout highway and run the bootstrap script. The bootstrap script is building highway using Swift Package Manager. The built highway command line tool will be placed (alongside other stuff) in `~/.highway`. Make sure to add `~/.highway/bin` to your `$PATH` after the bootstrap process is finished.\n\n## \u003ca name=\"setup\" /\u003eSetting up highway\nAfter installing highway make sure to add `~/.highway/bin` to your `$PATH`. Here is one way to do it:\n\n```\n$ echo \"PATH=$PATH:${HOME}/.highway/bin\" \u003e\u003e ${HOME}/.profile\n```\n\nThen open a new terminal window, so that the new `PATH` takes effect. Check your installation:\n\n```\n$ highway --version\n$ highway help\n```\n\n## \u003ca name=\"hello-world\" /\u003eHello World (the fun part)\nWith highway you can do just about anything. Highway is not specifically made for a specific task. You can think of it as a command line tool (+ support frameworks) that make is easy to manage, build and execute Swift code from the command line. One use case that comes immediately to mind is to use highway to build, test, deploy and release iOS/macOS apps.\n\nOpen Terminal and create an empty directory:\n\n```\n$ mkdir my_new_project\n$ cd my_new_project\n```\n\nNow you can create a plain highway project.:\n\n```\n$ highway init\n```\n\nThis creates a directory directory named `_highway`. You can have a look at the contents of the directory if you want. It is just a Swift Package Manager compatible project with a single Swift file, `main.swift`. In the directory containing the `_highway` directory execute:\n\n```\n$ highway\n```\n\nYes: Without any arguments. This builds and runs your `_highway`. By default, this displays a list of available commands. The cool thing is that you can add new commands by simply editing the `_highway/main.swift` file.\n\n# \u003ca name=\"make-useful\" /\u003e Make your highway project useful\n\n## Creating a simple custom Highway\nThe `highway` command line tool can be executed with arguments - just like most command line tools. Custom highways make it possible to invoke `highway` with custom commands/arguments. You can think of a highway as code that maps arguments/commands to custom logic, written in Swift. Each new highway project (created using `highway init`) comes with a few custom highways by default. You can see the predefined custom highways in `_highway/main.swift`. The default custom highways look something like this:\n\n``` swift\nimport HighwayCore\nimport XCBuild\nimport HighwayProject\nimport Deliver\nimport Foundation\n\nenum Way: String, HighwayType {\n    case test, build, run\n    var usage: String {\n        switch self {\n        case .build: return \"Builds the project\"\n        case .test: return \"Executes tests\"\n        case .run: return \"Runs the project\"\n        }\n    }\n}\n\nclass App: Highway\u003cWay\u003e {\n    override func setupHighways() {\n        self[.build] ==\u003e build\n        self[.test] ==\u003e test\n        self[.run] ==\u003e run\n    }\n\n    // MARK: - Highways\n    func build() {\n\n    }\n\n    func test() throws {\n        var options = TestOptions()\n        options.project = \"\u003cinsert path to *.xcproject here\u003e\"\n        options.scheme = \"\u003cinsert name of scheme here\u003e\"\n        options.destination = Destination.simulator(.iOS, name: \"iPhone 7\", os: .latest, id: nil)\n        try xcbuild.buildAndTest(using: options)\n    }\n\n    func run() {\n\n    }\n}\n\nApp(Way.self).go()\n```\n\nThe `Way`-enum implements the `HighwayType`-protocol. By default the `rawValue` of the `Way`-enum will be the expected argument. In the example above the highways `build`, `test` and `run` can be invoked like this:\n\nExecuting the `build`-highway:\n```\n$ highway build\n```\n\nExecuting the `test`-highway:\n```\n$ highway test\n```\n\nExecuting the `run`-highway:\n```\n$ highway run\n```\n\nYou get the idea. Creating a custom highway is done in two steps:\n\n**1. Add a case to the `Highway`-implementation and implement `-usage`:**\n\n``` swift\n//...\nenum Way: String, HighwayType {\n    case test\n    case build\n    case run\n    case myHighway // ⬅︎ HERE\n\n    var usage: String {\n        switch self {\n        case .build: return \"Builds the project\"\n        case .test: return \"Executes tests\"\n        case .run: return \"Runs the project\"\n        case .myHighway: return \"hello\" // ⬅︎ HERE\n        }\n    }\n}\n//...\n```\n\n**2. Register your highway:**\nStill in `_highway/main.swift`: Scroll down and register your highway in `-setupHighways` like this:\n\n``` swift\n//...\nclass App: Highway\u003cWay\u003e {\n    override func setupHighways() {\n        self[.build] ==\u003e build\n        self[.test] ==\u003e test\n        self[.run] ==\u003e run\n        self[.myHighway] ==\u003e myHighway // ⬅︎ HERE\n    }\n\n    func myHighway() throws { // ⬅︎ HERE\n      print(\"hello world\")\n    }\n//...\n```\nNow you can execute your highway like this:\n\n```\n$ highway myHighway\n```\n\nIf you omit the command/highway then highway will list all available commands/highways.\n\n## Highways with Dependencies\n\n### Depending on a single Highway\n\nA highway can depend on other highways. You specify dependencies between highways in your implementation of `-setupHighways`:\n\n``` swift\n//...\noverride func setupHighways() {\n    // imagine 'build' actually builds your app/project.\n    self[.build] ==\u003e build\n\n    // imagine 'test' actually runs your tests.\n    self[.test].depends(on: .build) ==\u003e test\n\n    // imagine 'run 'actually runs your app/project.\n    self[.run].depends(on: .test) ==\u003e run\n}\n//...\n```\n\nIn the example above there are three highways, two of which depend on other highways.\n\n* `test` depends on `build`: This means that executing `highway test` first executes the `build` highway. Which makes sense because you want to execute the tests only if your project is building.\n* `run` depends on `test`: This means that executing `highway run` first executes the `test` highway (which first executes the `build` highway). This way it is ensured that `highway run` always runs the latest artifact.\n\n### Depending on multiple Highways\n\nA highway can depend on a single highway (see above) or on multiple highways. Multiple dependencies are specified by using the exact same method (`-depend(on:)`). For example:\n\n``` swift\n//...\noverride func setupHighways() {\n    // imagine 'build' actually builds your app/project.\n    self[.build] ==\u003e build\n\n    // imagine 'test' actually runs your tests.\n    self[.test] ==\u003e test\n\n    // imagine 'run 'actually runs your app/project.\n    self[.run].depends(on: .build, .test) ==\u003e run\n}\n//...\n```\nThe `run`-highway above directly depends on `build` and `test`. This means that `highway run` will first execute `build` and then `test`.\n\n## Highways with Results\n\nA highway usually performs a task like building, testing, deploying (or something less impactful). It is not uncommon that a highway produces some kind of (intermediate) result. One example immediately comes to mind:\n\nLet's assume that you have two highways:\n\n1. `build`: Builds your project - for example by using `xcodebuild` or `swift`.\n2. `release`: Releases your project by uploading the build artifact to a server.\n\nYour `release`-highway naturally depends on `build`: `build` must be executed before `release` and `release` needs the results from the build process. You can do that by combining dependencies and highways with results:\n\n``` swift\nimport HighwayCore\nimport XCBuild\nimport HighwayProject\nimport Deliver\nimport Foundation\n\nenum Way: String, HighwayType {\n    case build, release\n}\n\nclass App: Highway\u003cWay\u003e {\n    override func setupHighways() {\n        self[.build] ==\u003e build\n        self[.release].depends(on: .build) ==\u003e release\n    }\n\n    func build() -\u003e String {\n        return \"./.build/release/my_app\"\n    }\n\n    func release() throws {\n        let path: String = try result(for: .build)\n        print(path)\n        // Now upload the file at path to a server.\n    }\n}\n\nApp(Way.self).go()\n```  \n\n## Special Highways\nThere are a few special highways. Registering for a special highway is straight forward:\n\n```swift\n//...\noverride func setupHighways() {\n  self[.build] ==\u003e build\n  self[.release].depends(on: .build) ==\u003e release\n\n  // 🔆 🔆 SPECIAL HIGHWAYS 🔆 🔆\n  onError = { print($0) }                         // 1.\n  onEmptyCommand = { print(\"$ ./_highway\") }      // 2.\n  onUnrecognizedCommand = { print(\"args: \\($0)\")} // 3.\n}\n//...\n```\n\nThe code above registers for three special highways:\n\n1. `onError`: If a custom highway throws an `Swift.Error` this highway is executed. The error is passed as an argument.\n2. `onEmptyCommand`: If the `_highway` binary is executed without any command this highway is executed. Don't confuse `highway` with `_highway`. `highway` is the command line tool you interact with all the time. `_highway` is the executable produced by compiling `_highway/main.swift`. Usually you do never interact with `_highway` directly. You only use `_highway` indirectly. It is only listed here to be complete.\n3. `onUnrecognizedCommand`: If `highway` is executed with a command that is neither handled by itself nor by your `_highway` project the default behavior is that `highway` prints a list of all known commands — unless you have registered the `onUnrecognizedCommand` highway. In that case `highway` no longer prints any helpful information when it encounters an unknown command.\n\n# Included Frameworks\n\nhighway comes with a few frameworks that help you get stuff done in your highway project. You are not limited to only those frameworks/features. You can use almost any Objective-C/Swift framework available. Just add additional frameworks you wanna use in your `_highway/main.swift`-file to `_highway/Package.swift`. However in some circumstances, the build in features/frameworks will be enough. Here is what you can use out of the box:\n\n## Swift: build \u0026 test\nExample:\n\n```\nlet swift = SwiftBuildSystem()\n\ntry swift.test() // Test\n\n// Build and get an object describing the result.\nlet artifact = try swift.build()\n\nprint(\"😜  \\(artifact.binPath)\")\nprint(\"😜  \\(artifact.buildOutput)\")\n```\n\n`SwiftBuildSystem` is a wrapper around the `swift` command line tool. By default, `test()` simply executes the tests of the Swift project in `_highway/..`. `build()` is much like `test()` with the difference that it returns a `SwiftBuildSystem.Artifact` that has properties like `buildOutput` (everything `swift build` would have printed to the screen) and `binPath`, the path to the directory that contains the built executable.\n\n## Xcode: build, archive, test (...) + xcpretty\nhighway has a few classes that deal with Xcode (a wrapper for xcodebuild and xcpretty). They can be found in the `XCBuild` framework.\n\nThose classes can be used to easily talk to the Xcode build system. However they are very volatile and subject to change.\n\nEach `Highway` automatically has an instance of `XCBuild`. `XCBuild` is able to:\n\n- build your Xcode project,\n- run your tests,\n- create archives,\n- code sign and export those archives and\n- finally upload them to iTunes Connect\n\nFor example the following code (that can be used in `setupHighways` as a highway) builds and tests the project `myapp`.\n\n``` swift\nfunc build() throws -\u003e TestReport {\n    var options = TestOptions()\n\n    options.project = cwd.appending(\"myapp.xcodeproj\").path\n    options.destination = Destination.simulator(.iOS,\n                                                name: \"iPhone 7\",\n                                                os: .latest,\n                                                id: nil)\n    options.scheme = \"myapp\"\n\n    return try xcbuild.buildAndTest(using: options)\n}\n```\n\n## Keychain: Access Secrets\nA simple way to store secrets and use the from within your custom highways is to use `Keychain`. You could store your old fashioned and highly insecure FTP-passwords there for example. Highway comes with a simple class that allows you to access your keychain:\n\n``` swift\nlet keychain = Keychain()\nlet query = Keychain.PasswordQuery(account: \"$username\", service: \"My FTP Password\")\nlet password = try keychain.password(matching: query)\n```\n\n## git: commit, push, (auto-) tag\n\n``` swift\nlet git = self.git // each highway has a git-instance already!\nlet cwd = self.cwd // each highway knows it's current working directory\n\n// Executes: git add .\ntry git.addEverything(at: cwd)\n\n// Executes: git commit -m \"$message\"\ntry git.commit(at: cwd, message: \"$message\")\n\n// Executes: git push origin master\ntry git.pushToMaster(at: cwd)\n\n// Executes: git-autotag\nlet nextVersion = try GitAutotag().autotag(at: cwd, dryRun: false)\n\n// Executes: git push --tags\ntry git.pushTagsToMaster(at: cwd)\n```\n\n## fastlane\nA super minimalistic wrapper around fastlane:\n\n```\ntry Fastlane().gym(\"arguments\", \"passed\", \"to\", \"fastlane gym\")\ntry Fastlane().scan(\"arguments\", \"passed\", \"to\", \"fastlane scan\")\n```\n\n# Updating\n\n## Dependencies\n\nUpdate the dependencies of your highway project:\n\n```\n$ highway clean\n$ highway\n$ highway generate # optional\n```\n\n## CLI\n\nUpdate highway itself:\n\n```\n$ highway self_update\n$ highway --version # verify\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChristianKienle%2Fhighway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FChristianKienle%2Fhighway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChristianKienle%2Fhighway/lists"}