-
-
Notifications
You must be signed in to change notification settings - Fork 958
Gem Transfers #5758
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gem Transfers #5758
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #5758 +/- ##
==========================================
+ Coverage 97.15% 97.18% +0.03%
==========================================
Files 460 469 +9
Lines 9559 9676 +117
==========================================
+ Hits 9287 9404 +117
Misses 272 272 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving comments which I'll return to as I continue this review.
t.string :status, null: false, default: "pending" | ||
t.references :organization, null: true, foreign_key: { to_table: :organizations } | ||
t.references :created_by, null: false, foreign_key: { to_table: :users } | ||
t.references :rubygem, null: false, foreign_key: { to_table: :rubygems } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 My understanding of a transfer is that it's updating ownership from one entity to another. In that model, I might be using a from_organization
and a to_organization
. This data could be used for auditing. What do you think of capturing this information? Does it fit our needs?
<% @title = "Create an Organization" %> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔎 There are a few instances of @title
being set in views. What do you think of moving assignment to the controller?
self.invites = users_for_rubygem.map { |user| existing_invites[user.id] || OrganizationInduction.new(user: user) } | ||
end | ||
|
||
def rubygem_ownership |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💅 What do you think of #authorized_to_give?
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose of the rubygem_ownership
is a bit different from what you would expect a predicate method to do. Maybe we can move the policy check into it's own method, but that feels unnecessary right now?
errors.add(:rubygem, "does not have permission to transfer this gem") | ||
end | ||
|
||
def organization_ownership |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💅 What do you think of #authorized_to_receive?
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thought as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been thinking about this a bunch and wanted to propose a model which I think might cover the needs we have and extend beyond them nicely. There are still a couple of cases that would need to be ironed out, but I think they're within reach.
Ownership Model
- A Rubygem is owned by a User OR an Organization
- An Organization has many members (Users)
- These members have access based on their roles in the org
- A Rubygem optionally has contributors (via Contributorships)
- contributors have a default set of permissions, though they could be extended
- The owner of the Rubygem would manage contributors
Assumptions
- If a Rubygem is owned by a User, the user has all permissions (admin, owner, etc)
- Permissions in an organization are dependent on the role within the organization
- Permissions for contributors are limited and do not (?) overlap with organization membership permissions
Further work
- If a contributor joins an organization:
- Leave contributorship as is? (cascade permissions, so we never have to worry about hitting this)
- Soft-delete contributorship?
- If a member leaves an organization:
- Organization owner determines whether to retain contributorship
erDiagram
User {
int id
string name
string email
}
Organization {
int id
string name
string description
}
Rubygem {
int id
string name
string version
int owner_id
string owner_type
}
Membership {
int user_id
int organization_id
string role
datetime joined_at
}
Contributorship {
int user_id
int rubygem_id
string permission_level
datetime granted_at
}
%% Core ownership relationships
User ||--o{ Rubygem : "owns (individual)"
Organization ||--o{ Rubygem : "owns (organizational)"
%% Organization structure
User ||--o{ Membership : "belongs to"
Organization ||--o{ Membership : "has members"
%% Gem contributorships (outside contributors only)
User ||--o{ Contributorship : "contributes to"
Rubygem ||--o{ Contributorship : "has contributors"
%% Notes on roles and permissions
%% Membership.role: owner, admin, member
%% Contributorship.permission_level: maintainer, contributor
%% Rubygem.owner_type: User or Organization
|
||
before_save :sync_invites, if: :organization_changed? | ||
|
||
def transfer! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 I wonder about #finalize!
or #complete!
as a name here. Since the transfer process is multi-step, the name seems incomplete (and potentially redundant with RubygemTransfer
being the class). What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transfer!
to me communicates the intent of what this big method does well. You don't have to dive into the 50 lines of code to figure out what it's doing.
@landongrindheim ad the Ownership Model, can't we make it simpler and make implicit organization per user so gem is always owned by organziation? This will make later planned changes like namespacing easier to introduce, since there will be only one way to own gem.
I don't think that's good name. Contributors is term used for users contributing to the codebase (like on GitHub) not related to release process. I think "maintainer" or "release manager" would be better name. |
@simi This was my initial thought. Something like
I agree the name is lacking. My assumption is that organizations will typically map to businesses or similar, and that there will be a need for including someone who doesn't belong to the business and shouldn't have the ability to take actions for all the organization's resources. I'm trying to capture that role. Is that a good fit for "maintainer" or "release manager"? If not, do you know of another term? |
@landongrindheim I think we can just make implicit organization for everyone named same as profile name (handle). We can also make implicit organization per gem 🤔. The original problem was related to fact, you were able to only add owner to gem and owner had all permission, so in theory the new owner was able to remove you as original owner and fully take over the gem. Usually gem owners were looking for someone to help (be able to release new versions) without risk of losing control over the gem. That's how Maintainer role was born (#4883). The second problem was you need to do this gem by gem. Some projects do often maintain big amount of gems (like here, each folder is separate gem https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/75bab36aa8901b40bf2bc5017581302d581ed0ea/instrumentation) and it is hard to maintain permissions in bulk. That's what Organizations should fix. And the final part is to ensure it all works together. In my thinking the organization is more like entity owning group of gems + their maintainers and owners under one umbrella. OpenTelemetry Ruby projects is hosting 2 kind of gems (per my understanding), core gems and contrib gems. I assume they will create 2 organizations for this on RubyGems.org to be able to add different maintainers per each type. |
…e polymorphic association
Co-authored-by: Landon Grindheim <landon.grindheim@gmail.com>
…sInvite#principal to #invitable
4037311
to
48b3772
Compare
This is primarily being done to keep us in compliance with CodeCov
This is primarily driven by getting CodeCov satisfied
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some tests to satisfy the CodeCov build. At the least I don't think they hurt. I think it's in a good place to move forward.
This Pull Request is the last remaining feature needed to begin private beta testing of Organizations in rubygems.org! This Pull Request introduces a system for a Rubygem to transition from being owned by a set of Users to being owned by an Organization.
Gem transfers work very similarly to Organization Onboardings in that they take a user through a multi-step process that asks for information like:
Breaking Change: OrganizationOnboardingInvite
Back in #5201, I introduced a table/model called
OrganizationOnboardingInvite,
which captured the User and associated role for the Organization Onboarding system. This model works great, and I've reused the same functionality here.To reduce duplication, I'm deprecating and deleting (the empty) the
OrganizationOnboardingInvite
model and moving everything toOrganizationInduction
that will work for both Organization Onboardings & Gem Transfers.Screenshots
User Management
Select an Organization
Gem Transfer Button/Link
Confirmation Screen