Skip to content

Commit 78dffb7

Browse files
committed
Merge remote-tracking branch 'upstream/master' into require-owner-approval
2 parents d771e1f + 696e6bb commit 78dffb7

15 files changed

+136
-74
lines changed

app/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<noscript>
2121
<div id="main">
2222
<div class='noscript'>
23-
This site requires that JavaScript to be enabled.
23+
This site requires JavaScript to be enabled.
2424
</div>
2525
</div>
2626
</noscript>

app/templates/index.hbs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div id='title-header'>
2-
<h1>The Rust community&rsquo;s crate host</h1>
2+
<h1>The Rust community&rsquo;s crate registry</h1>
33

44
<div class='links'>
55
{{#link-to "install" class='yellow-button'}}

src/krate/mod.rs

+48-35
Original file line numberDiff line numberDiff line change
@@ -462,50 +462,62 @@ impl Crate {
462462
req_user: &User,
463463
login: &str,
464464
) -> CargoResult<String> {
465-
let owner = match Owner::find_by_login(conn, login) {
466-
Ok(owner @ Owner::User(_)) => owner,
467-
Ok(Owner::Team(team)) => if team.contains_user(app, req_user)? {
468-
Owner::Team(team)
469-
} else {
470-
return Err(human(&format_args!(
471-
"only members of {} can add it as \
472-
an owner",
473-
login
474-
)));
475-
},
476-
Err(err) => if login.contains(':') {
477-
Owner::Team(Team::create(app, conn, login, req_user)?)
478-
} else {
479-
return Err(err);
480-
},
481-
};
465+
use diesel::insert;
482466

483-
let owner_invitation = NewCrateOwnerInvitation {
484-
invited_user_id: owner.id(),
485-
invited_by_user_id: req_user.id,
486-
crate_id: self.id,
487-
};
467+
let owner = Owner::find_or_create_by_login(app, conn, req_user, login)?;
488468

489-
diesel::insert(&owner_invitation.on_conflict_do_nothing())
490-
.into(crate_owner_invitations::table)
491-
.execute(conn)?;
469+
match owner {
470+
// Users are invited and must accept before being added
471+
owner @ Owner::User(_) => {
472+
let owner_invitation = NewCrateOwnerInvitation {
473+
invited_user_id: owner.id(),
474+
invited_by_user_id: req_user.id,
475+
crate_id: self.id,
476+
};
492477

493-
Ok(format!(
494-
"user {} has been invited to be an owner of crate {}",
495-
owner.login(),
496-
self.name
497-
))
478+
diesel::insert(&owner_invitation.on_conflict_do_nothing())
479+
.into(crate_owner_invitations::table)
480+
.execute(conn)?;
481+
482+
Ok(format!(
483+
"user {} has been invited to be an owner of crate {}",
484+
owner.login(),
485+
self.name
486+
))
487+
},
488+
// Teams are added as owners immediately
489+
owner @ Owner::Team(_) => {
490+
let crate_owner = CrateOwner {
491+
crate_id: self.id,
492+
owner_id: owner.id(),
493+
created_by: req_user.id,
494+
owner_kind: OwnerKind::Team as i32,
495+
};
496+
497+
insert(&crate_owner.on_conflict(
498+
crate_owners::table.primary_key(),
499+
do_update().set(crate_owners::deleted.eq(false)),
500+
)).into(crate_owners::table)
501+
.execute(conn)?;
502+
503+
Ok(format!(
504+
"team {} has been added as an owner of crate {}",
505+
owner.login(),
506+
self.name
507+
))
508+
}
509+
}
498510
}
499511

500512
pub fn owner_remove(
501513
&self,
514+
app: &App,
502515
conn: &PgConnection,
503-
_req_user: &User,
516+
req_user: &User,
504517
login: &str,
505518
) -> CargoResult<()> {
506-
let owner = Owner::find_by_login(conn, login).map_err(|_| {
507-
human(&format_args!("could not find owner with login `{}`", login))
508-
})?;
519+
let owner = Owner::find_or_create_by_login(app, conn, req_user, login)?;
520+
509521
let target = crate_owners::table.find((self.id(), owner.id(), owner.kind() as i32));
510522
diesel::update(target)
511523
.set(crate_owners::deleted.eq(true))
@@ -1363,6 +1375,7 @@ pub fn remove_owners(req: &mut Request) -> CargoResult<Response> {
13631375
fn modify_owners(req: &mut Request, add: bool) -> CargoResult<Response> {
13641376
let mut body = String::new();
13651377
req.body().read_to_string(&mut body)?;
1378+
13661379
let user = req.user()?;
13671380
let conn = req.db_conn()?;
13681381
let krate = Crate::by_name(&req.params()["crate_id"]).first::<Crate>(&*conn)?;
@@ -1408,7 +1421,7 @@ fn modify_owners(req: &mut Request, add: bool) -> CargoResult<Response> {
14081421
if owners.len() == 1 {
14091422
return Err(human("cannot remove the sole owner of a crate"));
14101423
}
1411-
krate.owner_remove(&conn, user, login)?;
1424+
krate.owner_remove(req.app(), &conn, user, login)?;
14121425
}
14131426
}
14141427

src/owner.rs

+21-16
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ pub struct Team {
4444
/// We only query membership with github using the github_id, though.
4545
/// This is the only name we should ever talk to Cargo about.
4646
pub login: String,
47-
/// We're assuming these are stable
47+
/// The GitHub API works on team ID numbers. This can change, if a team
48+
/// is deleted and then recreated with the same name!!!
4849
pub github_id: i32,
4950
/// Sugary goodness
5051
pub name: Option<String>,
@@ -107,7 +108,7 @@ impl<'a> NewTeam<'a> {
107108
use diesel::insert;
108109
use diesel::pg::upsert::*;
109110

110-
insert(&self.on_conflict(teams::github_id, do_update().set(self)))
111+
insert(&self.on_conflict(teams::login, do_update().set(self)))
111112
.into(teams::table)
112113
.get_result(conn)
113114
.map_err(Into::into)
@@ -116,7 +117,7 @@ impl<'a> NewTeam<'a> {
116117

117118
impl Team {
118119
/// Tries to create the Team in the DB (assumes a `:` has already been found).
119-
pub fn create(
120+
pub fn create_or_update(
120121
app: &App,
121122
conn: &PgConnection,
122123
login: &str,
@@ -135,7 +136,7 @@ impl Team {
135136
format is github:org:team",
136137
)
137138
})?;
138-
Team::create_github_team(app, conn, login, org, team, req_user)
139+
Team::create_or_update_github_team(app, conn, login, org, team, req_user)
139140
}
140141
_ => Err(human(
141142
"unknown organization handler, \
@@ -144,10 +145,10 @@ impl Team {
144145
}
145146
}
146147

147-
/// Tries to create a Github Team from scratch. Assumes `org` and `team` are
148+
/// Tries to create or update a Github Team. Assumes `org` and `team` are
148149
/// correctly parsed out of the full `name`. `name` is passed as a
149150
/// convenience to avoid rebuilding it.
150-
fn create_github_team(
151+
fn create_or_update_github_team(
151152
app: &App,
152153
conn: &PgConnection,
153154
login: &str,
@@ -292,18 +293,22 @@ fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User) -> Carg
292293
}
293294

294295
impl Owner {
295-
/// Finds the owner by name, failing out if it doesn't exist.
296-
/// May be a user's GH login, or a full team name. This is case
296+
/// Finds the owner by name. Always recreates teams to get the most
297+
/// up-to-date GitHub ID. Fails out if the user isn't found in the
298+
/// database, the team isn't found on GitHub, or if the user isn't a member
299+
/// of the team on GitHub.
300+
/// May be a user's GH login or a full team name. This is case
297301
/// sensitive.
298-
pub fn find_by_login(conn: &PgConnection, name: &str) -> CargoResult<Owner> {
302+
pub fn find_or_create_by_login(
303+
app: &App,
304+
conn: &PgConnection,
305+
req_user: &User,
306+
name: &str,
307+
) -> CargoResult<Owner> {
299308
if name.contains(':') {
300-
teams::table
301-
.filter(teams::login.eq(name))
302-
.first(conn)
303-
.map(Owner::Team)
304-
.map_err(|_| {
305-
human(&format_args!("could not find team with name {}", name))
306-
})
309+
Ok(Owner::Team(
310+
Team::create_or_update(app, conn, name, req_user)?,
311+
))
307312
} else {
308313
users::table
309314
.filter(users::gh_login.eq(name))

src/tests/http-data/team_add_owners_as_team_owner

+1-1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)