|
1 | 1 | use crate::background_jobs::{
|
2 | 2 | Environment, IndexAddCrateJob, IndexSyncToHttpJob, IndexUpdateYankedJob, Job, NormalizeIndexJob,
|
3 | 3 | };
|
| 4 | +use crate::models; |
4 | 5 | use crate::schema;
|
5 | 6 | use crate::swirl::PerformError;
|
6 | 7 | use anyhow::Context;
|
7 | 8 | use cargo_registry_index::{Crate, Repository};
|
8 | 9 | use chrono::Utc;
|
9 | 10 | use diesel::prelude::*;
|
10 |
| -use std::fs::{self, OpenOptions}; |
11 |
| -use std::io::{BufRead, BufReader, ErrorKind}; |
| 11 | +use std::fs::{self, File, OpenOptions}; |
| 12 | +use std::io::{BufRead, BufReader, ErrorKind, Write}; |
12 | 13 | use std::process::Command;
|
13 | 14 |
|
14 | 15 | #[instrument(skip_all, fields(krate.name = ?krate.name, krate.vers = ?krate.vers))]
|
@@ -74,6 +75,98 @@ pub fn update_crate_index(crate_name: String) -> Job {
|
74 | 75 | Job::IndexSyncToHttp(IndexSyncToHttpJob { crate_name })
|
75 | 76 | }
|
76 | 77 |
|
| 78 | +/// Regenerates or removes an index file for a single crate |
| 79 | +#[instrument(skip_all, fields(krate.name = ?krate))] |
| 80 | +pub fn sync_to_git_index( |
| 81 | + env: &Environment, |
| 82 | + conn: &mut PgConnection, |
| 83 | + krate: &str, |
| 84 | +) -> Result<(), PerformError> { |
| 85 | + info!("Syncing to git index"); |
| 86 | + |
| 87 | + let new = get_index_data(krate, conn).context("Failed to get index data")?; |
| 88 | + |
| 89 | + let repo = env.lock_index()?; |
| 90 | + let dst = repo.index_file(krate); |
| 91 | + |
| 92 | + // Read the previous crate contents |
| 93 | + let old = match fs::read_to_string(&dst) { |
| 94 | + Ok(content) => Some(content), |
| 95 | + Err(error) if error.kind() == ErrorKind::NotFound => None, |
| 96 | + Err(error) => return Err(error.into()), |
| 97 | + }; |
| 98 | + |
| 99 | + match (old, new) { |
| 100 | + (None, Some(new)) => { |
| 101 | + fs::create_dir_all(dst.parent().unwrap())?; |
| 102 | + let mut file = File::create(&dst)?; |
| 103 | + file.write_all(new.as_bytes())?; |
| 104 | + repo.commit_and_push(&format!("Creating crate `{}`", krate), &dst)?; |
| 105 | + } |
| 106 | + (Some(old), Some(new)) if old != new => { |
| 107 | + let mut file = File::create(&dst)?; |
| 108 | + file.write_all(new.as_bytes())?; |
| 109 | + repo.commit_and_push(&format!("Updating crate `{}`", krate), &dst)?; |
| 110 | + } |
| 111 | + (Some(_old), None) => { |
| 112 | + fs::remove_file(&dst)?; |
| 113 | + repo.commit_and_push(&format!("Deleting crate `{}`", krate), &dst)?; |
| 114 | + } |
| 115 | + _ => debug!("Skipping sync because index is up-to-date"), |
| 116 | + } |
| 117 | + |
| 118 | + Ok(()) |
| 119 | +} |
| 120 | + |
| 121 | +/// Regenerates or removes an index file for a single crate |
| 122 | +#[instrument(skip_all, fields(krate.name = ?krate))] |
| 123 | +pub fn sync_to_sparse_index( |
| 124 | + env: &Environment, |
| 125 | + conn: &mut PgConnection, |
| 126 | + krate: &str, |
| 127 | +) -> Result<(), PerformError> { |
| 128 | + info!("Syncing to sparse index"); |
| 129 | + |
| 130 | + let content = get_index_data(krate, conn).context("Failed to get index data")?; |
| 131 | + |
| 132 | + env.uploader |
| 133 | + .sync_index(env.http_client(), krate, content) |
| 134 | + .context("Failed to sync index data")?; |
| 135 | + |
| 136 | + if let Some(cloudfront) = env.cloudfront() { |
| 137 | + let path = Repository::relative_index_file_for_url(krate); |
| 138 | + |
| 139 | + info!(%path, "Invalidating index file on CloudFront"); |
| 140 | + cloudfront |
| 141 | + .invalidate(env.http_client(), &path) |
| 142 | + .context("Failed to invalidate CloudFront")?; |
| 143 | + } |
| 144 | + |
| 145 | + Ok(()) |
| 146 | +} |
| 147 | + |
| 148 | +#[instrument(skip_all, fields(krate.name = ?name))] |
| 149 | +pub fn get_index_data(name: &str, conn: &mut PgConnection) -> anyhow::Result<Option<String>> { |
| 150 | + debug!("Looking up crate by name"); |
| 151 | + let Some(krate): Option<models::Crate> = models::Crate::by_exact_name(name).first(conn).optional()? else { |
| 152 | + return Ok(None); |
| 153 | + }; |
| 154 | + |
| 155 | + debug!("Gathering remaining index data"); |
| 156 | + let crates = krate |
| 157 | + .index_metadata(conn) |
| 158 | + .context("Failed to gather index metadata")?; |
| 159 | + |
| 160 | + debug!("Serializing index data"); |
| 161 | + let mut bytes = Vec::new(); |
| 162 | + cargo_registry_index::write_crates(&crates, &mut bytes) |
| 163 | + .context("Failed to serialize index metadata")?; |
| 164 | + |
| 165 | + let str = String::from_utf8(bytes).context("Failed to decode index metadata as utf8")?; |
| 166 | + |
| 167 | + Ok(Some(str)) |
| 168 | +} |
| 169 | + |
77 | 170 | /// Yanks or unyanks a crate version. This requires finding the index
|
78 | 171 | /// file, deserlialise the crate from JSON, change the yank boolean to
|
79 | 172 | /// `true` or `false`, write all the lines back out, and commit and
|
|
0 commit comments