{"id":13878422,"url":"https://github.com/rubyatscale/code_ownership","last_synced_at":"2026-04-18T01:08:17.012Z","repository":{"id":37845869,"uuid":"490409122","full_name":"rubyatscale/code_ownership","owner":"rubyatscale","description":"A gem to help engineering teams declare ownership of code","archived":false,"fork":false,"pushed_at":"2025-05-16T20:11:20.000Z","size":7177,"stargazers_count":104,"open_issues_count":2,"forks_count":26,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-06-11T15:14:31.708Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","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/rubyatscale.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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}},"created_at":"2022-05-09T18:57:45.000Z","updated_at":"2025-05-17T13:50:16.000Z","dependencies_parsed_at":"2023-11-08T04:41:02.662Z","dependency_job_id":"2276151f-d27e-4d25-8fea-3216dfec44ef","html_url":"https://github.com/rubyatscale/code_ownership","commit_stats":{"total_commits":37,"total_committers":5,"mean_commits":7.4,"dds":"0.10810810810810811","last_synced_commit":"db898859142feeed09ef88358ae8103e488be5c3"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"purl":"pkg:github/rubyatscale/code_ownership","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubyatscale%2Fcode_ownership","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubyatscale%2Fcode_ownership/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubyatscale%2Fcode_ownership/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubyatscale%2Fcode_ownership/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rubyatscale","download_url":"https://codeload.github.com/rubyatscale/code_ownership/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubyatscale%2Fcode_ownership/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260739051,"owners_count":23055074,"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":[],"created_at":"2024-08-06T08:01:49.144Z","updated_at":"2026-04-18T01:08:16.999Z","avatar_url":"https://github.com/rubyatscale.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# CodeOwnership\n\nThis gem helps engineering teams declare ownership of code. This gem works best in large, usually monolithic code bases where many teams work together.\n\nCheck out [`lib/code_ownership.rb`](https://github.com/rubyatscale/code_ownership/blob/main/lib/code_ownership.rb) to see the public API.\n\nCheck out [`code_ownership_spec.rb`](https://github.com/rubyatscale/code_ownership/blob/main/spec/lib/code_ownership_spec.rb) to see examples of how code ownership is used.\n\nThere is also a [companion VSCode Extension](https://github.com/rubyatscale/code-ownership-vscode) for this gem. Just search `Gusto.code-ownership-vscode` in the VSCode Extension Marketplace.\n\n## Getting started\n\nTo get started there are a few things you should do.\n\n1. Create a `config/code_ownership.yml` file and declare where your files live. Here's a sample to start with:\n\n```yml\nowned_globs:\n  - '{app,components,config,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx}'\njs_package_paths: []\nunowned_globs:\n  - db/**/*\n  - app/services/some_file1.rb\n  - app/services/some_file2.rb\n  - frontend/javascripts/**/__generated__/**/*\n```\n\n2. Declare some teams. Here's an example, that would live at `config/teams/operations.yml`:\n\n```yml\nname: Operations\ngithub:\n  team: '@my-org/operations-team'\n```\n\n3. Declare ownership. You can do this at a directory level or at a file level. All of the files within the `owned_globs` you declared in step 1 will need to have an owner assigned (or be opted out via `unowned_globs`). See the next section for more detail.\n4. Run validations when you commit, and/or in CI. If you run validations in CI, ensure that if your `.github/CODEOWNERS` file gets changed, that gets pushed to the PR.\n\n## Usage: Declaring Ownership\n\nThere are five ways to declare code ownership using this gem:\n\n### Directory-Based Ownership\n\nDirectory-based ownership allows for all files in that directory and all its sub-directories to be owned by one team. To define this, add a `.codeowner` file inside that directory with the name of the team as the contents of that file.\n\n```\nTeam\n```\n\n### File-Annotation Based Ownership\n\nFile annotations are a last resort if there is no clear home for your code. File annotations go at the top of your file, and look like this:\n\n```ruby\n# @team MyTeam\n```\n\n### Package-Based Ownership\n\nPackage-based ownership integrates [`packwerk`](https://github.com/Shopify/packwerk) and has ownership defined per package. To define that all files within a package are owned by one team, configure your `package.yml` like this:\n\n```yml\nenforce_dependency: true\nenforce_privacy: true\nmetadata:\n  owner: Team\n```\n\nYou can also define `owner` as a top-level key, e.g.\n\n```yml\nenforce_dependency: true\nenforce_privacy: true\nowner: Team\n```\n\nTo do this, add `code_ownership` to the `require` key of your `packwerk.yml`. See \u003chttps://github.com/Shopify/packwerk/blob/main/USAGE.md#loading-extensions\u003e for more information.\n\n### Glob-Based Ownership\n\nIn your team's configured YML (see [`code_teams`](https://github.com/rubyatscale/code_teams)), you can set `owned_globs` to be a glob of files your team owns. For example, in `my_team.yml`:\n\n```yml\nname: My Team\nowned_globs:\n  - app/services/stuff_belonging_to_my_team/**/**\n  - app/controllers/other_stuff_belonging_to_my_team/**/**\nunowned_globs:\n  - app/controllers/other_stuff_belonging_to_my_team/that_one_weird_dir_we_dont_own/*\n```\n\n### Javascript Package Ownership\n\nJavaScript package-based ownership allows you to specify an ownership key in a `package.json`. To use this, configure your `package.json` like this:\n\n```json\n{\n  // other keys\n  \"metadata\": {\n    \"owner\": \"My Team\"\n  }\n  // other keys\n}\n```\n\nYou can also tell `code_ownership` where to find JS packages in the configuration, like this:\n\n```yml\njs_package_paths:\n  - frontend/javascripts/packages/*\n  - frontend/other_location_for_packages/*\n```\n\nThis defaults to `**/`, which makes it look for `package.json` files across your application.\n\n\u003e [!NOTE]\n\u003e JavaScript package ownership does not respect `unowned_globs`. If you wish to disable usage of this feature you can set `js_package_paths` to an empty list.\n\n```yml\njs_package_paths: []\n```\n\n## Usage: Reading CodeOwnership\n\n### `for_file`\n\n`CodeOwnership.for_file`, given a relative path to a file returns a `CodeTeams::Team` if there is a team that owns the file, `nil` otherwise.\n\n```ruby\nCodeOwnership.for_file('path/to/file/relative/to/application/root.rb')\n```\n\nContributor note: If you are making updates to this method or the methods getting used here, please benchmark the performance of the new implementation against the current for both `for_files` and `for_file` (with 1, 100, 1000 files).\n\nSee `code_ownership_spec.rb` for examples.\n\n### `for_backtrace`\n\n`CodeOwnership.for_backtrace` can be given a backtrace and will either return `nil`, or a `CodeTeams::Team`.\n\n```ruby\nCodeOwnership.for_backtrace(exception.backtrace)\n```\n\nThis will go through the backtrace, and return the first found owner of the files associated with frames within the backtrace.\n\nSee `code_ownership_spec.rb` for an example.\n\n### `for_class`\n\n`CodeOwnership.for_class` can be given a class and will either return `nil`, or a `CodeTeams::Team`.\n\n```ruby\nCodeOwnership.for_class(MyClass)\n```\n\nUnder the hood, this finds the file where the class is defined and returns the owner of that file.\n\nSee `code_ownership_spec.rb` for an example.\n\n### `for_team`\n\n`CodeOwnership.for_team` can be used to generate an ownership report for a team.\n\n```ruby\nCodeOwnership.for_team('My Team')\n```\n\nYou can shovel this into a markdown file for easy viewing using the CLI:\n\n```\ncodeownership for_team 'My Team' \u003e tmp/ownership_report.md\n```\n\n## Usage: Generating a `CODEOWNERS` file\n\nA `CODEOWNERS` file defines who owns specific files or paths in a repository. When you run `codeownership validate`, a `.github/CODEOWNERS` file will automatically be generated and updated.\n\nIf `codeowners_path` is set in `code_ownership.yml` codeowners will use that path to generate the `CODEOWNERS` file. For example, `codeowners_path: docs` will generate `docs/CODEOWNERS`.\n\n## Proper Configuration \u0026 Validation\n\nCodeOwnership comes with a validation function to ensure the following things are true:\n\n1. Only one mechanism is defining file ownership. That is -- you can't have a file annotation on a file owned via package-based or glob-based ownership. This helps make ownership behavior more clear by avoiding concerns about precedence.\n2. All teams referenced as an owner for any file or package are valid teams (i.e. they're in the list of `CodeTeams.all`).\n3. All files have ownership. You can specify in `unowned_globs` to represent a TODO list of files to add ownership to.\n4. The `.github/CODEOWNERS` file is up to date. This is automatically corrected and staged unless specified otherwise with `bin/codeownership validate --skip-autocorrect --skip-stage`. You can turn this validation off by setting `skip_codeowners_validation: true` in `config/code_ownership.yml`.\n\nCodeOwnership also allows you to specify which globs and file extensions should be considered ownable.\n\nHere is an example `config/code_ownership.yml`.\n\n```yml\nowned_globs:\n  - '{app,components,config,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx}'\nunowned_globs:\n  - db/**/*\n  - app/services/some_file1.rb\n  - app/services/some_file2.rb\n  - frontend/javascripts/**/__generated__/**/*\n```\n\nYou can call the validation function with the Ruby API\n\n```ruby\nCodeOwnership.validate!\n```\n\nor the CLI\n\n```bash\n# Validate all files\ncodeownership validate\n\n# Validate specific files\ncodeownership validate path/to/file1.rb path/to/file2.rb\n\n# Validate only staged files\ncodeownership validate --diff\n```\n\n## Development\n\nPlease add to `CHANGELOG.md` and this `README.md` when you make changes.\n\n## Running specs\n\n```sh\nbundle install\nbundle exec rake\n```\n\n## Creating a new release\n\nSimply [create a new release](https://github.com/rubyatscale/code_ownership/releases/new) with github. The release tag must match the gem version\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubyatscale%2Fcode_ownership","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frubyatscale%2Fcode_ownership","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubyatscale%2Fcode_ownership/lists"}