Skip to content

Commit dc30868

Browse files
committed
Use new crates format
Bump to 0.3.0 for breaking change Fail when old crate format is detected Add new use_new_crates_format flag in mirror.toml Add warning when flag is set, but new format is detected Update nginx sample config Update rewrite function
1 parent 74087e8 commit dc30868

8 files changed

+109
-7
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "panamax"
3-
version = "0.2.3"
3+
version = "0.3.0"
44
authors = ["k3d3 <[email protected]>"]
55
description = "Mirror rustup and crates.io repositories, for offline Rust and Cargo usage."
66
license = "MIT/Apache-2.0"

nginx.sample.conf

+7-1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,11 @@ server {
2424
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
2525
fastcgi_param GIT_HTTP_EXPORT_ALL "";
2626
fastcgi_param PATH_INFO $1;
27-
}
27+
}
28+
29+
# Rewrite the download URLs to match the proper crates location.
30+
rewrite "^/crates/([^/])/([^/]+)$" "/crates/1/$1/$2" last;
31+
rewrite "^/crates/([^/]{2})/([^/]+)$" "/crates/2/$1/$2" last;
32+
rewrite "^/crates/([^/])([^/]{2})/([^/]+)$" "/crates/3/$1/$1$2/$3" last;
33+
rewrite "^/crates/([^/]{2})([^/]{2})([^/]*)/([^/]+)$" "/crates/$1/$2/$1$2$3/$4" last;
2834
}

src/crates.rs

+68-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use git2::Repository;
55
use reqwest::header::HeaderValue;
66
use scoped_threadpool::Pool;
77
use serde::{Deserialize, Serialize};
8-
use std::path::Path;
8+
use std::fs::read_dir;
9+
use std::path::{Path, PathBuf};
910
use std::{
1011
fs,
1112
io::{self, BufRead, Cursor},
@@ -56,11 +57,38 @@ pub fn sync_one_crate_entry(
5657
)
5758
};
5859

60+
// let file_path = path
61+
// .join("crates")
62+
// .join(&crate_entry.name)
63+
// .join(&crate_entry.vers)
64+
// .join("download");
65+
66+
let crate_name = format!("{}-{}.crate", &crate_entry.name, &crate_entry.vers);
67+
68+
let crate_path = match crate_entry.name.len() {
69+
1 => PathBuf::from("1"),
70+
2 => PathBuf::from("2"),
71+
3 => PathBuf::from("3"),
72+
n if n >= 4 => {
73+
let first_two = &crate_entry
74+
.name
75+
.get(0..2)
76+
.expect("crate name len >= 4 but couldn't get first 2 chars");
77+
let second_two = &crate_entry
78+
.name
79+
.get(2..4)
80+
.expect("crate name len >= 4 but couldn't get second 2 chars");
81+
[first_two, second_two].iter().collect()
82+
}
83+
_ => return Err(DownloadError::BadCrate("Empty crate name".into())),
84+
};
85+
5986
let file_path = path
6087
.join("crates")
88+
.join(crate_path)
6189
.join(&crate_entry.name)
6290
.join(&crate_entry.vers)
63-
.join("download");
91+
.join(crate_name);
6492

6593
download(
6694
&url[..],
@@ -185,7 +213,8 @@ pub fn sync_crates_files(
185213
// Get the data for this crate file
186214
let oid = df.id();
187215
if oid.is_zero() {
188-
// The crate was removed, continue to next crate
216+
// The crate was removed, continue to next crate.
217+
// Note that this does not include yanked crates.
189218
removed_crates.push(p.to_path_buf());
190219
return true;
191220
}
@@ -250,3 +279,39 @@ pub fn sync_crates_files(
250279

251280
Ok(())
252281
}
282+
283+
/// Detect if the crates directory is using the old format.
284+
pub fn is_new_crates_format(path: &Path) -> Result<bool, io::Error> {
285+
for crate_dir in read_dir(path)? {
286+
let crate_dir = crate_dir?;
287+
if !crate_dir.file_type()?.is_dir() {
288+
// Ignore any files in the directory. Only look at other directories.
289+
continue;
290+
}
291+
292+
let dir_name = crate_dir
293+
.file_name()
294+
.into_string()
295+
.map_err(|_| io::ErrorKind::Other)?;
296+
match dir_name.as_str() {
297+
"1" | "2" | "3" => continue, // 1-letter crate names cannot be numbers, so this must be new format.
298+
x if x.len() == 2 => {
299+
// Verify that this isn't a 2-char crate, by checking for 2 char crates inside.
300+
// In the old format, this would contain version numbers, which are almost never 2 chars long.
301+
for inner_dir in read_dir(path.join(crate_dir.file_name()))? {
302+
let inner_dir = inner_dir?;
303+
if inner_dir.file_name().len() != 2 {
304+
// Not the new crates format.
305+
return Ok(false);
306+
}
307+
}
308+
}
309+
_ => {
310+
// Unrecognized directory found, might be crate in old format.
311+
return Ok(false);
312+
}
313+
};
314+
}
315+
316+
Ok(true)
317+
}

src/crates_index.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ pub fn rewrite_config_json(repo_path: &Path, base_url: &str) -> Result<(), Index
147147

148148
let mut index = repo.index()?;
149149

150+
let crate_path = format!("{}/{}", base_url, "{crate}/{crate}-{version}.crate");
151+
150152
// Create the new config.json.
151153
let config_json = ConfigJson {
152-
dl: base_url.to_string(),
154+
dl: crate_path,
153155
api: base_url.to_string(),
154156
};
155157
let contents = serde_json::to_vec_pretty(&config_json)?;

src/download.rs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub enum DownloadError {
1313
Io(#[from] io::Error),
1414
#[error("HTTP download error: {0}")]
1515
Download(#[from] reqwest::Error),
16+
#[error("Got bad crate: {0}")]
17+
BadCrate(String),
1618
#[error("Mismatched hash - expected '{expected}', got '{actual}'")]
1719
MismatchedHash { expected: String, actual: String },
1820
#[error("HTTP not found. Status: {status}, URL: {url}, data: {data}")]

src/mirror.default.toml

+3
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ source = "https://crates.io/api/v1/crates"
164164
# Where to clone the crates.io-index repository from.
165165
source_index = "https://github.com/rust-lang/crates.io-index"
166166

167+
# Set to true if the 0.3 crates directory format is being used.
168+
use_new_crates_format = true
169+
167170
# URL where this mirror's crates directory can be accessed from.
168171
# Used for rewriting crates.io-index's config.json.
169172
# Remove this parameter to perform no rewriting.

src/mirror.rs

+24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use reqwest::header::HeaderValue;
66
use serde::{Deserialize, Serialize};
77
use thiserror::Error;
88

9+
use crate::crates::is_new_crates_format;
910
use crate::crates_index::rewrite_config_json;
1011

1112
#[derive(Error, Debug)]
@@ -43,6 +44,7 @@ pub struct ConfigCrates {
4344
pub download_threads: usize,
4445
pub source: String,
4546
pub source_index: String,
47+
pub use_new_crates_format: Option<bool>,
4648
pub base_url: Option<String>,
4749
}
4850

@@ -121,6 +123,28 @@ pub fn sync(path: &Path) -> Result<(), MirrorError> {
121123
}
122124
let mirror = load_mirror_toml(path)?;
123125

126+
// Fail if use_new_crates_format is not true, and old format is detected.
127+
// If use_new_crates_format is true and new format is detected, warn the user.
128+
// If use_new_crates_format is true, ignore the format and assume it's new.
129+
if let Some(crates) = &mirror.crates {
130+
if crates.sync {
131+
if crates.use_new_crates_format != Some(true) {
132+
if is_new_crates_format(&path.join("crates"))? {
133+
eprintln!("Your crates/ directory is using the new 0.3 format, however");
134+
eprintln!("use_new_crates_format is not set in mirror.toml. To remove this warning,");
135+
eprintln!("Please add 'use_new_crates_format = true' to mirror.toml's [crates] section.");
136+
eprintln!();
137+
} else {
138+
eprintln!("Your crates directory is using the old 0.2 format, however");
139+
eprintln!("Panamax 0.3 has deprecated this format for a new one.");
140+
eprintln!("Please delete crates/ and crates.io-index/ from your mirror to continue,");
141+
eprintln!("and add 'use_new_crates_format = true' to mirror.toml's [crates] section.");
142+
return Ok(());
143+
}
144+
}
145+
}
146+
}
147+
124148
// Handle the contact information
125149

126150
let user_agent_str = if let Some(ref contact) = mirror.mirror.contact {

0 commit comments

Comments
 (0)