{"id":20495653,"url":"https://github.com/alexander-ignition/xstooling","last_synced_at":"2025-06-22T19:38:35.383Z","repository":{"id":46370811,"uuid":"415664099","full_name":"Alexander-Ignition/XSTooling","owner":"Alexander-Ignition","description":"Xcode + Swift toolset","archived":false,"fork":false,"pushed_at":"2023-01-30T10:44:46.000Z","size":46,"stargazers_count":31,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-27T08:45:12.736Z","etag":null,"topics":["bash","shell","simctl","xcrun"],"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/Alexander-Ignition.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}},"created_at":"2021-10-10T18:11:02.000Z","updated_at":"2024-10-08T13:18:35.000Z","dependencies_parsed_at":"2023-02-16T06:31:28.449Z","dependency_job_id":null,"html_url":"https://github.com/Alexander-Ignition/XSTooling","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Ignition%2FXSTooling","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Ignition%2FXSTooling/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Ignition%2FXSTooling/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexander-Ignition%2FXSTooling/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Alexander-Ignition","download_url":"https://codeload.github.com/Alexander-Ignition/XSTooling/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248756715,"owners_count":21156813,"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":["bash","shell","simctl","xcrun"],"created_at":"2024-11-15T17:46:47.100Z","updated_at":"2025-04-13T17:44:07.204Z","avatar_url":"https://github.com/Alexander-Ignition.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🛠 XSTooling 🧰\n\n[![Test](https://github.com/Alexander-Ignition/XSTooling/actions/workflows/test.yml/badge.svg)](https://github.com/Alexander-Ignition/XSTooling/actions/workflows/test.yml)\n[![SPM compatible](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager)\n[![Swift 5.3](https://img.shields.io/badge/swift-5.3-brightgreen.svg?style=flat)](https://developer.apple.com/swift)\n[![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/Alexander-Ignition/XSTooling/blob/master/LICENSE)\n\nXcode and Swift toolset\n\nSupported tools:\n\n- shell\n- xcrun\n- simstl\n\n## Getting Started\n\nTo use the `XSTooling` library in a SwiftPM project, add the following line to the dependencies in your `Package.swift` file:\n\n```swift\n.package(url: \"https://github.com/Alexander-Ignition/XSTooling\", from: \"0.0.2\"),\n```\n\nInclude `\"XSTooling\"` as a dependency for your executable target:\n\n```swift\n.target(name: \"\u003ctarget\u003e\", dependencies: [\n    \"XSTooling\",\n]),\n```\n\nFinally, add `import XSTooling` to your source code.\n\n```swift\nimport XSTooling\n\nlet sh = Shell.default\ntry await sh(\"swift build\").run()\n```\n\n## Shell\n\nShell command can be `run` or `read`.\n\nRead the shell command output.\n\n```swift\nlet version = try await sh(\"xcodebuild -version\").read().string\n```\n\nRun shell command with redirection to stdout and stderr.\n\n```swift\ntry await sh(\"ls -al\").run()\n```\n\nRedirection can be configured, for example, to write to a log file.\n\n```swift\nlet url = URL(fileURLWithPath: \"logs.txt\", isDirectory: false)\nFileManager.default.createFile(atPath: url.path, contents: nil)\nlet file = try FileHandle(forWritingTo: url)\n\ntry await sh(\"swift build\").run(.output(file).error(file))\n```\n\n`Shell` has predefined instances.\n\n```swift\nShell.default\nShell.sh\nShell.bash\nShell.zsh\n```\n\nConceptually, a `Shell` is a wrapper over a `ProcessCommand`. \n\n- `sh.command` contains common parameters for all commands.\n- `sh(\"ls\")` each call to this method returned a copy of the `ProcessCommand` with additional arguments\n\n```swift\nlet sh = Shell.default\nsh.command // ProcessCommand\nsh.command.environment // [String: String]?\nsh.command.currentDirectoryURL // URL?\nsh(\"ls\") // ProcessCommand\n```\n\n## ProcessCommand\n\nThe main component is `ProcessCommand`. Which can configure and run a subprocess. The `read` and `run` methods are called on the `ProcessCommand`.\n\n```swift\nlet command = ProcessCommand(\n    path: \"/usr/bin/xcodebuild\",\n    arguments: [\"-version\"]\n)\ntry await command.run()\n```\n\nThe location of the executable file is not always known. To do this, there is a `find` method that searches for an executable file by name.\n\n```swift\ntry await ProcessCommand\n    .find(\"xcodebuild\")!\n    .appending(argument: \"-version\")\n    .run()\n```\n\n## simctl\n\nBy analogy with Shell, you can make other wrappers over the `ProcessCommand`\n\n`Simctl` (Simulator control tool) is an example of a complex such wrapper\n\nUsing simctl, you can search for iPhone 12, turn it on and launch the application.\n\n```swift\nlet xcrun = XCRun()\nlet simulator = xcrun.simctl\n\nlet list = try await simulator.list(.devices, \"iPhone 12\", available: true).json.decode()\nlet devices = list.devices.flatMap { $0.value }\n\nfor info in devices where info.state == \"Booted\" {\n    try await simulator.device(info.udid).shutdown.run()\n}\n            \nlet udid =  devices.first!.udid\ntry await simulator.device(udid).boot.run()\ntry await simulator.device(udid).app(\"com.example.app\").launch.run()\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexander-ignition%2Fxstooling","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexander-ignition%2Fxstooling","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexander-ignition%2Fxstooling/lists"}