Skip to content

Commit aa3712b

Browse files
committed
Check if a crate is already yanked/unyanked in the job
Right now we check if a crate is already yanked/unyanked before enqueing the job at all. This means that if you try to undo a yank before the yank finishes running, we won't enqueue the unyank at all. This isn't an expected scenario, and we still might not do the unyank if the jobs are run out of order, but this means that we should always end up in the expected state under normal operation.
1 parent 9c168e4 commit aa3712b

File tree

2 files changed

+49
-36
lines changed

2 files changed

+49
-36
lines changed

src/controllers/version/yank.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ fn modify_yank(req: &mut dyn Request, yanked: bool) -> CargoResult<Response> {
3636
return Err(human("must already be an owner to yank or unyank"));
3737
}
3838

39-
if version.yanked != yanked {
40-
git::yank(&conn, krate.name, version, yanked)?;
41-
}
39+
git::yank(&conn, krate.name, version, yanked)?;
4240

4341
#[derive(Serialize)]
4442
struct R {

src/git.rs

+48-33
Original file line numberDiff line numberDiff line change
@@ -168,39 +168,54 @@ impl Job for Yank {
168168
let repo = Repository::open(&env.index_location)?;
169169
let dst = repo.index_file(&self.krate);
170170

171-
let prev = fs::read_to_string(&dst)?;
172-
let version = self.version.num.to_string();
173-
let new = prev
174-
.lines()
175-
.map(|line| {
176-
let mut git_crate = serde_json::from_str::<Crate>(line)
177-
.map_err(|_| internal(&format_args!("couldn't decode: `{}`", line)))?;
178-
if git_crate.name != self.krate || git_crate.vers != version {
179-
return Ok(line.to_string());
180-
}
181-
git_crate.yanked = Some(self.yanked);
182-
Ok(serde_json::to_string(&git_crate)?)
183-
})
184-
.collect::<CargoResult<Vec<String>>>();
185-
let new = new?.join("\n") + "\n";
186-
fs::write(&dst, new.as_bytes())?;
187-
188-
repo.commit_and_push(
189-
&format!(
190-
"{} crate `{}#{}`",
191-
if self.yanked { "Yanking" } else { "Unyanking" },
192-
self.krate,
193-
self.version.num
194-
),
195-
&repo.relative_index_file(&self.krate),
196-
env.credentials(),
197-
)?;
198-
199-
diesel::update(&self.version)
200-
.set(versions::yanked.eq(self.yanked))
201-
.execute(&*env.connection()?)?;
202-
203-
Ok(())
171+
let conn = env.connection()?;
172+
173+
conn.transaction(|| {
174+
let yanked_in_db = versions::table
175+
.find(self.version.id)
176+
.select(versions::yanked)
177+
.for_update()
178+
.first::<bool>(&*conn)?;
179+
180+
if yanked_in_db == self.yanked {
181+
// The crate is alread in the state requested, nothing to do
182+
return Ok(());
183+
}
184+
185+
let prev = fs::read_to_string(&dst)?;
186+
let version = self.version.num.to_string();
187+
let new = prev
188+
.lines()
189+
.map(|line| {
190+
let mut git_crate = serde_json::from_str::<Crate>(line)
191+
.map_err(|_| internal(&format_args!("couldn't decode: `{}`", line)))?;
192+
if git_crate.name != self.krate || git_crate.vers != version {
193+
return Ok(line.to_string());
194+
}
195+
git_crate.yanked = Some(self.yanked);
196+
Ok(serde_json::to_string(&git_crate)?)
197+
})
198+
.collect::<CargoResult<Vec<String>>>();
199+
let new = new?.join("\n") + "\n";
200+
fs::write(&dst, new.as_bytes())?;
201+
202+
repo.commit_and_push(
203+
&format!(
204+
"{} crate `{}#{}`",
205+
if self.yanked { "Yanking" } else { "Unyanking" },
206+
self.krate,
207+
self.version.num
208+
),
209+
&repo.relative_index_file(&self.krate),
210+
env.credentials(),
211+
)?;
212+
213+
diesel::update(&self.version)
214+
.set(versions::yanked.eq(self.yanked))
215+
.execute(&*conn)?;
216+
217+
Ok(())
218+
})
204219
}
205220
}
206221

0 commit comments

Comments
 (0)