{"id":32146524,"url":"https://github.com/sdidla/xcresultowners","last_synced_at":"2026-02-19T02:02:14.052Z","repository":{"id":303670682,"uuid":"1016292926","full_name":"sdidla/xcresultowners","owner":"sdidla","description":"This project supplements the test results summary produced by xcresulttool with code ownership defined in GitHub's CODEOWNERS file.","archived":false,"fork":false,"pushed_at":"2025-08-18T19:58:48.000Z","size":91,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-21T08:49:04.507Z","etag":null,"topics":["codeowners","indexstore-db"],"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/sdidla.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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-08T19:31:38.000Z","updated_at":"2025-08-19T07:49:59.000Z","dependencies_parsed_at":"2025-07-24T00:19:03.012Z","dependency_job_id":null,"html_url":"https://github.com/sdidla/xcresultowners","commit_stats":null,"previous_names":["sdidla/xcresultowners"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/sdidla/xcresultowners","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdidla%2Fxcresultowners","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdidla%2Fxcresultowners/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdidla%2Fxcresultowners/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdidla%2Fxcresultowners/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sdidla","download_url":"https://codeload.github.com/sdidla/xcresultowners/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdidla%2Fxcresultowners/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29600845,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T00:59:38.239Z","status":"online","status_checked_at":"2026-02-19T02:00:07.702Z","response_time":117,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["codeowners","indexstore-db"],"created_at":"2025-10-21T08:10:52.829Z","updated_at":"2026-02-19T02:02:14.047Z","avatar_url":"https://github.com/sdidla.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Unit Tests](https://github.com/sdidla/xcresultowners/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/sdidla/xcresultowners/actions/workflows/unit-tests.yml)\n[![Documentation](https://github.com/sdidla/xcresultowners/actions/workflows/documentation.yml/badge.svg)](https://sdidla.github.io/xcresultowners/documentation/xcresultownerscore)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsdidla%2Fxcresultowners%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/sdidla/xcresultowners)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsdidla%2Fxcresultowners%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/sdidla/xcresultowners)\n[![GitHub license](https://img.shields.io/github/license/sdidla/xcresultowners)](https://github.com/sdidla/xcresultowners/blob/main/LICENSE)\n\n\u003cimg width=\"100\" alt=\"xcresultowners-logo\" src=\"https://github.com/user-attachments/assets/ce5f3f6c-8d48-4d69-bcaa-8d09e43e8493\" /\u003e\n\n# xcresultowners\n\nThis project supplements the test results summary produced by [`xcresulttool`](https://keith.github.io/xcode-man-pages/xcresulttool.1.html) with code ownership defined in GitHub's [`CODEOWNERS`](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) file. The package includes two products:\n\n- `xcresultowners`: The command line tool to identify owners of failed tests.\n- `XCResultOwnersCore`: The library that can be used to build your own macOS tool.\n\n## Installation\n\nInstall the command line tool using [Homebrew](https://brew.sh):\n\n```shell\nbrew install sdidla/formulae/xcresultowners\n```\n\n## Usage\n\nYou will need the path to `libIndexStore.dylib` that is installed with Xcode. This can be located using:\n\n```shell\nxcrun xcodebuild -find-library libIndexStore.dylib\n```\n\nYou will also need to locate the index store of your project. Both Xcode and Swift Package Manager, by default, create an index store before or while building your project.\n\n- For Xcode projects, by default it is located at `$HOME/Library/Developer/Xcode/DerivedData/\u003cprojectname\u003e-\u003chash\u003e/Index.noindex/DataStore`\n- For SPM projects, by default it is located at `./build/debug/index/store`\n\n\n### `xcresultowners`\n\n#### Supplement xcresult summary with owners\n```shell\n # Genrate a json file with xcresult summary\n xcrun xcresulttool get test-results summary --path \u003cpath-to-xcresult-bundle\u003e \u003e xcresult.json\n\n # Use xcresultowners to supplement code owners\n swift run xcresultowners \\\n   --library-path      $(xcrun xcodebuild -find-library libIndexStore.dylib) \\\n   --repository-path   \u003cpath-to-repository-containing-code-and-codeowners\u003e \\\n   --store-path        \u003cpath-to-project-index-store\u003e \\\n   --format            \u003cjson|markdown\u003e \\\n   xcresult.json\n\n```\n\n#### List owners of files\n```shell\n swift run xcresultowners file-owners --repository-path \u003cpath-to-repository\u003e\n```\n\n#### Locate tests using `IndexStoreDB`\n```shell\n swift run xcresultowners locate-test \\\n   --library-path            $(xcrun xcodebuild -find-library libIndexStore.dylib) \\\n   --store-path              \u003cpath-to-project-index-store\u003e \\\n   --module-name             \u003cmodule-name\u003e \\\n   --test-identifier-string  \u003ctest-identifier-string-from-xcresults-file\u003e\n```\n\n\n#### Comprehensive USAGE details\n```shell\nswift run xcresultowners help\n```\n\n### `XCResultOwnersCore`\n\n#### Resolving owners of all failures\n\n```swift\nimport XCResultOwnersCore\n\nlet xcResultSummary = try JSONDecoder().decode(XCResultSummary.self, from: xcResultSummaryJSON)\nasync let ownedFiles = resolveFileOwners(repositoryURL: repositoryFileURL)\nasync let indexStoreDB = IndexStoreDB(storePath: storePath, libraryPath: libraryPath)\n\nlet ownedFailures = try await resolveFailureOwners(\n    testFailures: xcResultSummary.testFailures,\n    ownedFiles: ownedFiles,\n    indexStoreDB: indexStoreDB\n)\n\nfor failure in ownedFailures {\n  print(\"original failure:\", failure.xcFailure)\n  print(\"path:\", failure.path)\n  print(\"owners:\", failure.owners)\n}\n```\n\n#### Identifying owners of a file\n\n```swift\nimport XCResultOwnersCore\n\nlet repositoryURL = URL(filePath: repositoryPath)\nlet ownedFiles = try await resolveFileOwners(repositoryURL: repositoryURL)\n\nfor file in ownedFiles {\n    print(file.fileURL)\n    print(file.owners)\n}\n```\n\n#### Locating Tests\n\n```swift\nimport XCResultOwnersCore\n\nlet indexStoreDB = try await IndexStoreDB(\n    storePath: storePath, \n    libraryPath: libraryPath\n)\n\nlet location = indexStoreDB.locate(\n    testIdentifierString: testIdentifierString,\n    moduleName: moduleName\n)\n\nprint(location.moduleName)\nprint(location.path)\nprint(location.line)\nprint(location.utf8Column)\n\n```\n\n#### API Documentation\n\n[Documentation](https://sdidla.github.io/xcresultowners/documentation/xcresultownerscore/)\n\n\n\n\n## Implementation Details\n\n1. In Xcode 16.3, Apple [updated](https://developer.apple.com/documentation/xcode-release-notes/xcode-16_3-release-notes#xcresulttool) `xcresulttool` to use a much improved JSON schema. The human-readable JSON summary can be printed using:\n   \n   ```shell\n   xcrun xcresulttool get test-results summary --path \u003cpath-to-xcresult-bundle\u003e \n   ```\n   \n   The output includes a `testIdentifierString` and `targetName` for each failing test case. \n1. Since the `testIdentifierString` is not a location of a file, this needs to be mapped to a file path. Under the hood, Xcode uses the open-sourced [indexstore-db](https://github.com/swiftlang/indexstore-db) project (available as a swift package) for features such as symbol lookups and code completion. This library can be leveraged to locate the test case represented by `testIdentifierString`. \n1. Once the location where the test case is defined is determined, we can use information (patterns and associated team mentions) in the GitHub `CODEOWNERS` file to generate a list of owners for every file in a repository and determine a definitive list of owners of the test case in question. For pattern matching (globbing), we use POSIX standard's [`fnmatch`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html) that is natively available in `Swift`.\n\n## Versioning\n\nThough this project uses [semantic versioning](https://semver.org), to be used as an SPM package dependency, you will have to use the `revision:` parameter as the underlying [`indexstore-db`](https://github.com/swiftlang/indexstore-db) project does not use strict semantic versioning.\n\n```swift\ndependencies: [\n   .package(url: \"https://github.com/sdidla/xcresultowners\", revision: \"\u003c#release-tag#\u003e\")\n],\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdidla%2Fxcresultowners","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsdidla%2Fxcresultowners","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdidla%2Fxcresultowners/lists"}