Skip to content

Commit ebbcfc3

Browse files
author
Alex Evanczuk
committed
Initial commit
1 parent a91d62a commit ebbcfc3

36 files changed

+2570
-1
lines changed

.github/workflows/ci.yml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
ruby:
11+
- 2.7
12+
# See comment comes from https://github.com/ruby/setup-ruby#matrix-of-ruby-versions
13+
# Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
14+
- '3.0'
15+
- 3.1
16+
- head
17+
env:
18+
BUNDLE_GEMFILE: Gemfile
19+
name: "Tests: Ruby ${{ matrix.ruby }}"
20+
steps:
21+
- uses: actions/checkout@5126516654c75f76bca1de45dd82a3006d8890f9
22+
- name: Set up Ruby ${{ matrix.ruby }}
23+
uses: ruby/setup-ruby@bd94d6a504586da892a5753afdd1480096ed30df
24+
with:
25+
ruby-version: ${{ matrix.ruby }}
26+
- name: Run tests
27+
run: |
28+
gem install bundler
29+
bundle install --jobs 4 --retry 3
30+
bundle exec rspec
31+
static-type-checking:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@5126516654c75f76bca1de45dd82a3006d8890f9
35+
- name: Set up Ruby
36+
uses: ruby/setup-ruby@bd94d6a504586da892a5753afdd1480096ed30df
37+
with:
38+
ruby-version: head
39+
- name: Run static type checks
40+
run: |
41+
gem install bundler
42+
bundle install --jobs 4 --retry 3
43+
bundle exec srb tc

.github/workflows/publish.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Publish Gem
2+
3+
on:
4+
push:
5+
branches:
6+
- "main"
7+
tags:
8+
- v*
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@5126516654c75f76bca1de45dd82a3006d8890f9
15+
16+
- name: Release Gem
17+
if: contains(github.ref, 'refs/tags/v')
18+
uses: cadwallion/publish-rubygems-action@8f9e0538302643309e4e43bf48cd34173ca48cfc
19+
env:
20+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
21+
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}

.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/.bundle/
2+
/.yardoc
3+
/_yardoc/
4+
/coverage/
5+
/doc/
6+
/pkg/
7+
/spec/reports/
8+
/tmp/
9+
10+
# rspec failure tracking
11+
.rspec_status

.rspec

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--format documentation
2+
--color
3+
--require spec_helper

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
See https://github.com/bigrails/code_ownership/releases

Gemfile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source 'https://rubygems.org'
2+
3+
gemspec

Gemfile.lock

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
PATH
2+
remote: .
3+
specs:
4+
code_ownership (1.23.0)
5+
bigrails-teams
6+
parse_packwerk
7+
sorbet-runtime
8+
9+
GEM
10+
remote: https://rubygems.org/
11+
specs:
12+
ast (2.4.2)
13+
bigrails-teams (0.1.0)
14+
sorbet-runtime
15+
coderay (1.1.3)
16+
diff-lcs (1.4.4)
17+
method_source (1.0.0)
18+
parse_packwerk (0.10.0)
19+
sorbet-runtime
20+
parser (3.1.2.0)
21+
ast (~> 2.4.1)
22+
pry (0.14.1)
23+
coderay (~> 1.1)
24+
method_source (~> 1.0)
25+
rake (13.0.6)
26+
rbi (0.0.14)
27+
ast
28+
parser (>= 2.6.4.0)
29+
sorbet-runtime (>= 0.5.9204)
30+
unparser
31+
rspec (3.10.0)
32+
rspec-core (~> 3.10.0)
33+
rspec-expectations (~> 3.10.0)
34+
rspec-mocks (~> 3.10.0)
35+
rspec-core (3.10.1)
36+
rspec-support (~> 3.10.0)
37+
rspec-expectations (3.10.1)
38+
diff-lcs (>= 1.2.0, < 2.0)
39+
rspec-support (~> 3.10.0)
40+
rspec-mocks (3.10.2)
41+
diff-lcs (>= 1.2.0, < 2.0)
42+
rspec-support (~> 3.10.0)
43+
rspec-support (3.10.2)
44+
sorbet (0.5.9889)
45+
sorbet-static (= 0.5.9889)
46+
sorbet-runtime (0.5.9889)
47+
sorbet-static (0.5.9889-universal-darwin-14)
48+
sorbet-static (0.5.9889-universal-darwin-15)
49+
sorbet-static (0.5.9889-universal-darwin-16)
50+
sorbet-static (0.5.9889-universal-darwin-17)
51+
sorbet-static (0.5.9889-universal-darwin-18)
52+
sorbet-static (0.5.9889-universal-darwin-19)
53+
sorbet-static (0.5.9889-universal-darwin-20)
54+
sorbet-static (0.5.9889-universal-darwin-21)
55+
sorbet-static (0.5.9889-x86_64-linux)
56+
spoom (1.1.11)
57+
sorbet (>= 0.5.9204)
58+
sorbet-runtime (>= 0.5.9204)
59+
thor (>= 0.19.2)
60+
tapioca (0.7.2)
61+
bundler (>= 1.17.3)
62+
pry (>= 0.12.2)
63+
rbi (~> 0.0.0, >= 0.0.14)
64+
sorbet-runtime (>= 0.5.9204)
65+
sorbet-static (>= 0.5.9204)
66+
spoom (~> 1.1.0, >= 1.1.11)
67+
thor (>= 1.2.0)
68+
yard-sorbet
69+
thor (1.2.1)
70+
unparser (0.6.4)
71+
diff-lcs (~> 1.3)
72+
parser (>= 3.1.0)
73+
webrick (1.7.0)
74+
yard (0.9.27)
75+
webrick (~> 1.7.0)
76+
yard-sorbet (0.6.1)
77+
sorbet-runtime (>= 0.5)
78+
yard (>= 0.9)
79+
80+
PLATFORMS
81+
arm64-darwin-20
82+
arm64-darwin-21
83+
ruby
84+
x86_64-darwin-20
85+
x86_64-linux
86+
87+
DEPENDENCIES
88+
code_ownership!
89+
pry
90+
rake
91+
rspec (~> 3.0)
92+
sorbet
93+
tapioca
94+
95+
BUNDLED WITH
96+
2.3.9

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Gusto
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+101-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,101 @@
1-
# code_ownership
1+
# CodeOwnership
2+
This gem helps engineering teams declare ownership of code.
3+
4+
Check out `lib/code_ownership.rb` to see the public API.
5+
6+
Check out `code_ownership_spec.rb` to see examples of how code ownership is used.
7+
8+
## Usage: Declaring Ownership
9+
There are three ways to declare code ownership using this gem.
10+
### Package-Based Ownership
11+
Package 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:
12+
```yml
13+
enforce_dependency: true
14+
enforce_privacy: true
15+
metadata:
16+
owner: Team
17+
```
18+
19+
### Glob-Based Ownership
20+
In your team's configured YML (see [`bigrails-teams`](https://github.com/bigrails/bigrails-teams)), you can set `owned_globs` to be a glob of files your team owns. For example, in `my_team.yml`:
21+
```yml
22+
name: My Team
23+
owned_globs:
24+
- app/services/stuff_belonging_to_my_team/**/**
25+
- app/controllers/other_stuff_belonging_to_my_team/**/**
26+
```
27+
### File-Annotation Based Ownership
28+
File 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:
29+
```ruby
30+
# @team MyTeam
31+
```
32+
## Usage: Reading CodeOwnership
33+
### `for_file`
34+
`CodeOwnership.for_file`, given a relative path to a file returns a `Teams::Team` if there is a team that owns the file, `nil` otherwise.
35+
36+
```ruby
37+
CodeOwnership.for_file('path/to/file/relative/to/application/root.rb')
38+
```
39+
40+
Contributor 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).
41+
42+
See `code_ownership_spec.rb` for examples.
43+
44+
### `for_backtrace`
45+
`CodeOwnership.for_backtrace` can be given a backtrace and will either return `nil`, or a `Teams::Team`.
46+
47+
```ruby
48+
CodeOwnership.for_backtrace(exception.backtrace)
49+
```
50+
51+
This will go through the backtrace, and return the first found owner of the files associated with frames within the backtrace.
52+
53+
See `code_ownership_spec.rb` for an example.
54+
55+
### `for_class`
56+
57+
`CodeOwnership.for_class` can be given a class and will either return `nil`, or a `Teams::Team`.
58+
59+
```ruby
60+
CodeOwnership.for_class(MyClass.name)
61+
```
62+
63+
Under the hood, this finds the file where the class is defined and returns the owner of that file.
64+
65+
See `code_ownership_spec.rb` for an example.
66+
67+
## Usage: Generating a `CODEOWNERS` file
68+
69+
A `CODEOWNERS` file defines who owns specific files or paths in a repository. When you run `bin/codeownership validate`, a `.github/CODEOWNERS` file will automatically be generated and updated.
70+
71+
## Proper Configuration & Validation
72+
CodeOwnership comes with a validation function to ensure the following things are true:
73+
1) 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.
74+
2) All teams referenced as an owner for any file or package is a valid team (i.e. it's in the list of `Teams.all`).
75+
3) All files have ownership. You can specify in `unowned_globs` to represent a TODO list of files to add ownership to.
76+
3) 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 `code_ownership.yml`.
77+
78+
CodeOwnership also allows you to specify which globs and file extensions should be considered ownable.
79+
80+
Here is an example `config/code_ownership.yml`.
81+
```yml
82+
owned_globs:
83+
- '{app,components,config,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx}'
84+
unowned_globs:
85+
- db/**/*
86+
- app/services/some_file1.rb
87+
- app/services/some_file2.rb
88+
- frontend/javascripts/**/__generated__/**/*
89+
```
90+
You can call the validation function with the Ruby API
91+
```ruby
92+
CodeOwnership.validate!
93+
```
94+
or the CLI
95+
```
96+
bin/codeownership validate
97+
```
98+
99+
## Development
100+
101+
Please add to `CHANGELOG.md` and this `README.md` when you make make changes.

bin/codeownership

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env ruby
2+
# typed: strict
3+
4+
require 'code_ownership'
5+
CodeOwnership::Cli.run!(ARGV)

code_ownership.gemspec

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Gem::Specification.new do |spec|
2+
spec.name = "code_ownership"
3+
spec.version = '1.23.0'
4+
spec.authors = ['Gusto Engineers']
5+
spec.email = ['[email protected]']
6+
spec.summary = 'A gem to help engineering teams declare ownership of code'
7+
spec.description = 'A gem to help engineering teams declare ownership of code'
8+
spec.homepage = 'https://github.com/bigrails/code_ownership'
9+
spec.license = 'MIT'
10+
11+
if spec.respond_to?(:metadata)
12+
spec.metadata['homepage_uri'] = spec.homepage
13+
spec.metadata['source_code_uri'] = 'https://github.com/bigrails/code_ownership'
14+
spec.metadata['changelog_uri'] = 'https://github.com/bigrails/code_ownership/releases'
15+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
16+
else
17+
raise 'RubyGems 2.0 or newer is required to protect against ' \
18+
'public gem pushes.'
19+
end
20+
# https://guides.rubygems.org/make-your-own-gem/#adding-an-executable
21+
# and
22+
# https://bundler.io/blog/2015/03/20/moving-bins-to-exe.html
23+
spec.executables = ['codeownership']
24+
25+
# Specify which files should be added to the gem when it is released.
26+
spec.files = Dir['README.md', 'sorbet/**/*', 'lib/**/*', 'bin/**/*']
27+
spec.require_paths = ['lib']
28+
29+
spec.add_dependency 'bigrails-teams'
30+
spec.add_dependency 'parse_packwerk'
31+
spec.add_dependency 'sorbet-runtime'
32+
33+
spec.add_development_dependency 'rake'
34+
spec.add_development_dependency 'pry'
35+
spec.add_development_dependency 'rspec', '~> 3.0'
36+
spec.add_development_dependency 'sorbet'
37+
spec.add_development_dependency 'tapioca'
38+
end

config/code_ownership.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
owned_globs:
2+
- '{app,components,config,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx}'

0 commit comments

Comments
 (0)