|
1 |
| -use std::{str::FromStr, sync::Arc}; |
| 1 | +use std::{ |
| 2 | + path::Path, |
| 3 | + str::FromStr, |
| 4 | + sync::{Arc, LazyLock}, |
| 5 | +}; |
2 | 6 |
|
3 | 7 | use crate::load::{partition_in_place, SiteCtxt};
|
4 |
| -use chrono::Utc; |
| 8 | +use chrono::{DateTime, NaiveDate, Utc}; |
5 | 9 | use database::{BenchmarkRequest, BenchmarkRequestStatus, BenchmarkRequestType};
|
6 | 10 | use hashbrown::HashSet;
|
7 | 11 | use parking_lot::RwLock;
|
| 12 | +use regex::Regex; |
8 | 13 | use tokio::time::{self, Duration};
|
9 | 14 |
|
10 | 15 | /// Store the latest master commits or do nothing if all of them are
|
@@ -36,6 +41,94 @@ async fn create_benchmark_request_master_commits(
|
36 | 41 | Ok(())
|
37 | 42 | }
|
38 | 43 |
|
| 44 | +/// Parses strings in the following formats extracting the Date & release tag |
| 45 | +/// `static.rust-lang.org/dist/2016-05-24/channel-rust-1.9.0.toml` |
| 46 | +/// `static.rust-lang.org/dist/2016-05-31/channel-rust-nightly.toml` |
| 47 | +/// `static.rust-lang.org/dist/2016-06-01/channel-rust-beta.toml` |
| 48 | +/// `static.rust-lang.org/dist/2025-06-26/channel-rust-1.89-beta.toml` |
| 49 | +/// `static.rust-lang.org/dist/2025-06-26/channel-rust-1.89.0-beta.toml` |
| 50 | +/// `static.rust-lang.org/dist/2025-06-26/channel-rust-1.89.0-beta.2.toml` |
| 51 | +fn parse_release_string(url: &str) -> Option<(String, DateTime<Utc>)> { |
| 52 | + static VERSION_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(\d+\.\d+\.\d+)").unwrap()); |
| 53 | + |
| 54 | + // Grab ".../YYYY-MM-DD/FILE.toml" components with Path helpers. |
| 55 | + let file = Path::new(url).file_name().and_then(|n| n.to_str())?; |
| 56 | + |
| 57 | + let date_str = Path::new(url) |
| 58 | + .parent() |
| 59 | + .and_then(Path::file_name) |
| 60 | + .and_then(|n| n.to_str())?; |
| 61 | + |
| 62 | + // No other beta releases are recognized as toolchains. |
| 63 | + // |
| 64 | + // We also have names like this: |
| 65 | + // |
| 66 | + // * channel-rust-1.75-beta.toml |
| 67 | + // * channel-rust-1.75.0-beta.toml |
| 68 | + // * channel-rust-1.75.0-beta.1.toml |
| 69 | + // |
| 70 | + // Which should get ignored for now, they're not consumable via rustup yet. |
| 71 | + if file.contains("beta") && file != "channel-rust-beta.toml" { |
| 72 | + return None; |
| 73 | + } |
| 74 | + |
| 75 | + // Parse the YYYY-MM-DD segment and stamp it with *current* UTC time. |
| 76 | + if let Ok(naive) = NaiveDate::parse_from_str(date_str, "%Y-%m-%d") { |
| 77 | + let published = naive |
| 78 | + .and_hms_opt(0, 0, 0) |
| 79 | + .expect("valid HMS") |
| 80 | + .and_local_timezone(Utc) |
| 81 | + .single() |
| 82 | + .unwrap(); |
| 83 | + |
| 84 | + // Special-case the rolling beta channel. |
| 85 | + if file == "channel-rust-beta.toml" { |
| 86 | + return Some((format!("beta-{date_str}"), published)); |
| 87 | + } |
| 88 | + |
| 89 | + // Otherwise pull out a semver like "1.70.0" and return it. |
| 90 | + if let Some(cap) = VERSION_RE.captures(file).and_then(|m| m.get(1)) { |
| 91 | + return Some((cap.as_str().to_owned(), published)); |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + None |
| 96 | +} |
| 97 | + |
| 98 | +/// Store the latest release commits or do nothing if all of them are |
| 99 | +/// already in the database |
| 100 | +async fn create_benchmark_request_releases( |
| 101 | + conn: &dyn database::pool::Connection, |
| 102 | +) -> anyhow::Result<()> { |
| 103 | + let releases: String = reqwest::get("https://static.rust-lang.org/manifests.txt") |
| 104 | + .await? |
| 105 | + .text() |
| 106 | + .await?; |
| 107 | + // TODO; delete at some point in the future |
| 108 | + let cutoff: chrono::DateTime<Utc> = chrono::DateTime::from_str("2025-06-01T00:00:00.000Z")?; |
| 109 | + |
| 110 | + let releases: Vec<_> = releases |
| 111 | + .lines() |
| 112 | + .rev() |
| 113 | + .filter_map(parse_release_string) |
| 114 | + .take(20) |
| 115 | + .collect(); |
| 116 | + |
| 117 | + for (name, date_time) in releases { |
| 118 | + if date_time >= cutoff { |
| 119 | + let release_request = BenchmarkRequest::create_release( |
| 120 | + &name, |
| 121 | + date_time, |
| 122 | + BenchmarkRequestStatus::ArtifactsReady, |
| 123 | + "", |
| 124 | + "", |
| 125 | + ); |
| 126 | + conn.insert_benchmark_request(&release_request).await; |
| 127 | + } |
| 128 | + } |
| 129 | + Ok(()) |
| 130 | +} |
| 131 | + |
39 | 132 | /// Sorts try and master requests that are in the `ArtifactsReady` status.
|
40 | 133 | /// Doesn't consider in-progress requests or release artifacts.
|
41 | 134 | fn sort_benchmark_requests(done: &HashSet<String>, request_queue: &mut [BenchmarkRequest]) {
|
@@ -186,6 +279,8 @@ async fn cron_enqueue_jobs(site_ctxt: &Arc<SiteCtxt>) -> anyhow::Result<()> {
|
186 | 279 | let mut conn = site_ctxt.conn().await;
|
187 | 280 | // Put the master commits into the `benchmark_requests` queue
|
188 | 281 | create_benchmark_request_master_commits(site_ctxt, &*conn).await?;
|
| 282 | + // Put the releases into the `benchmark_requests` queue |
| 283 | + create_benchmark_request_releases(&*conn).await?; |
189 | 284 | enqueue_next_job(&mut *conn).await?;
|
190 | 285 | Ok(())
|
191 | 286 | }
|
@@ -213,10 +308,21 @@ pub async fn cron_main(site_ctxt: Arc<RwLock<Option<Arc<SiteCtxt>>>>, seconds: u
|
213 | 308 | #[cfg(test)]
|
214 | 309 | mod tests {
|
215 | 310 | use super::*;
|
216 |
| - use chrono::{Datelike, Duration, TimeZone, Utc}; |
217 |
| - |
| 311 | + use chrono::{Datelike, Duration, NaiveDate, TimeZone, Utc}; |
218 | 312 | use database::tests::run_postgres_test;
|
219 | 313 |
|
| 314 | + /// Helper: unwrap the Option, panic otherwise. |
| 315 | + fn tag(url: &str) -> String { |
| 316 | + parse_release_string(url) |
| 317 | + .expect("Some") // Option<_> |
| 318 | + .0 // take the tag |
| 319 | + } |
| 320 | + |
| 321 | + /// Helper: unwrap the DateTime and keep only the YYYY-MM-DD part |
| 322 | + fn day(url: &str) -> NaiveDate { |
| 323 | + parse_release_string(url).expect("Some").1.date_naive() |
| 324 | + } |
| 325 | + |
220 | 326 | fn days_ago(day_str: &str) -> chrono::DateTime<Utc> {
|
221 | 327 | // Walk backwards until the first non-digit, then slice
|
222 | 328 | let days = day_str
|
@@ -602,4 +708,55 @@ mod tests {
|
602 | 708 | })
|
603 | 709 | .await;
|
604 | 710 | }
|
| 711 | + |
| 712 | + #[test] |
| 713 | + fn parses_stable_versions() { |
| 714 | + assert_eq!( |
| 715 | + tag("static.rust-lang.org/dist/2016-05-24/channel-rust-1.9.0.toml"), |
| 716 | + "1.9.0" |
| 717 | + ); |
| 718 | + assert_eq!( |
| 719 | + day("static.rust-lang.org/dist/2016-05-24/channel-rust-1.9.0.toml"), |
| 720 | + NaiveDate::from_ymd_opt(2016, 5, 24).unwrap() |
| 721 | + ); |
| 722 | + |
| 723 | + assert_eq!( |
| 724 | + tag("static.rust-lang.org/dist/2025-06-26/channel-rust-1.88.0.toml"), |
| 725 | + "1.88.0" |
| 726 | + ); |
| 727 | + assert_eq!( |
| 728 | + day("static.rust-lang.org/dist/2025-06-26/channel-rust-1.88.0.toml"), |
| 729 | + NaiveDate::from_ymd_opt(2025, 6, 26).unwrap() |
| 730 | + ); |
| 731 | + } |
| 732 | + |
| 733 | + #[test] |
| 734 | + fn parses_plain_beta_channel() { |
| 735 | + let want = "beta-2016-06-01"; |
| 736 | + let url = "static.rust-lang.org/dist/2016-06-01/channel-rust-beta.toml"; |
| 737 | + |
| 738 | + assert_eq!(tag(url), want); |
| 739 | + assert_eq!(day(url), NaiveDate::from_ymd_opt(2016, 6, 1).unwrap()); |
| 740 | + } |
| 741 | + |
| 742 | + #[test] |
| 743 | + fn skips_unconsumable_channels() { |
| 744 | + // nightly never returns Anything |
| 745 | + assert!(parse_release_string( |
| 746 | + "static.rust-lang.org/dist/2016-05-31/channel-rust-nightly.toml" |
| 747 | + ) |
| 748 | + .is_none()); |
| 749 | + |
| 750 | + // versioned-beta artefacts are skipped too |
| 751 | + for should_ignore in [ |
| 752 | + "static.rust-lang.org/dist/2025-06-26/channel-rust-1.89-beta.toml", |
| 753 | + "static.rust-lang.org/dist/2025-06-26/channel-rust-1.89.0-beta.toml", |
| 754 | + "static.rust-lang.org/dist/2025-06-26/channel-rust-1.89.0-beta.2.toml", |
| 755 | + ] { |
| 756 | + assert!( |
| 757 | + parse_release_string(should_ignore).is_none(), |
| 758 | + "{should_ignore} should be ignored" |
| 759 | + ); |
| 760 | + } |
| 761 | + } |
605 | 762 | }
|
0 commit comments