From a70e7b56ca245405fbb96cc5a5233c8f25aea268 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Tue, 13 Jan 2026 18:32:29 +0100 Subject: [PATCH 1/5] fix edge-case when local static/vendor files are deleted --- crates/bin/docs_rs_web/src/handlers/statics.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/bin/docs_rs_web/src/handlers/statics.rs b/crates/bin/docs_rs_web/src/handlers/statics.rs index 0c4abbb6a..746c67050 100644 --- a/crates/bin/docs_rs_web/src/handlers/statics.rs +++ b/crates/bin/docs_rs_web/src/handlers/statics.rs @@ -93,7 +93,17 @@ async fn conditional_get( } let mut res = next.run(req).await; - res.headers_mut().typed_insert(etag); + let status = res.status(); + // Typically we only end up here when we have a successful response. + // + // But there is an edge case, that only happens when there is a path + // where we were able to statically generate an ETag in the + // build-script, but the file can't be found later. + // Until now, happend only in local dev, but would also happen + // if the static file was deleted from the server after deployment. + if status.is_success() { + res.headers_mut().typed_insert(etag); + } res } From 1910acdb299e26f864a347a2612172e08f4fd1aa Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Tue, 13 Jan 2026 18:35:10 +0100 Subject: [PATCH 2/5] fix serving static/vendor files in local development --- .../bin/docs_rs_web/src/handlers/statics.rs | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/crates/bin/docs_rs_web/src/handlers/statics.rs b/crates/bin/docs_rs_web/src/handlers/statics.rs index 746c67050..abc51cf45 100644 --- a/crates/bin/docs_rs_web/src/handlers/statics.rs +++ b/crates/bin/docs_rs_web/src/handlers/statics.rs @@ -16,8 +16,31 @@ use axum_extra::{ use docs_rs_headers::IfNoneMatch; use docs_rs_mimes::APPLICATION_OPENSEARCH_XML; use http::{StatusCode, Uri}; +use std::{ + env, + path::{Path, PathBuf}, +}; use tower_http::services::ServeDir; +const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); + +/// Find the path to one of the static dirs. +/// +/// First, check if a directory with the given name exists in the CWD. +/// When it doesn't exist, we fall back to the CARGO_MANIFEST_DIR as root. +/// +/// This allows running the server from the project root. +fn static_dir(name: &str) -> PathBuf { + if let Ok(cwd) = env::current_dir() { + let candidate = cwd.join(name); + if candidate.is_dir() { + return candidate; + } + } + + Path::new(MANIFEST_DIR).join(name) +} + const VENDORED_CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/vendored.css")); const STYLE_CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/style.css")); const RUSTDOC_CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/rustdoc.css")); @@ -130,11 +153,13 @@ pub(crate) fn build_static_router() -> AxumRouter { get_static(|| async { build_static_css_response(RUSTDOC_2025_08_20_CSS) }), ) .fallback_service( - get_service(ServeDir::new("static").fallback(ServeDir::new("vendor"))) - .layer(middleware::from_fn(set_needed_static_headers)) - .layer(middleware::from_fn(|request, next| async { - request_recorder(request, next, Some("static resource")).await - })), + get_service( + ServeDir::new(static_dir("static")).fallback(ServeDir::new(static_dir("vendor"))), + ) + .layer(middleware::from_fn(set_needed_static_headers)) + .layer(middleware::from_fn(|request, next| async { + request_recorder(request, next, Some("static resource")).await + })), ) .layer(middleware::from_fn(conditional_get)) } @@ -313,12 +338,13 @@ mod tests { let web = env.web_app().await; for root in STATIC_SEARCH_PATHS { - for entry in walkdir::WalkDir::new(root) { + let root = static_dir(root); + for entry in walkdir::WalkDir::new(&root) { let entry = entry?; if !entry.file_type().is_file() { continue; } - let file = entry.path().strip_prefix(root).unwrap(); + let file = entry.path().strip_prefix(&root).unwrap(); let path = entry.path(); let url = format!("/-/static/{}", file.to_str().unwrap()); From e4a979c7bef59d43479453d70742f7c18b106cbb Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Fri, 16 Jan 2026 00:11:25 +0100 Subject: [PATCH 3/5] add test for broken local static asset directories --- Cargo.lock | 1 + crates/bin/docs_rs_web/Cargo.toml | 1 + crates/bin/docs_rs_web/src/handlers/mod.rs | 2 +- .../bin/docs_rs_web/src/handlers/statics.rs | 99 +++++++++++++++---- crates/bin/docs_rs_web/src/routes.rs | 12 ++- 5 files changed, 91 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 559f7965c..db3c3f00d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2447,6 +2447,7 @@ dependencies = [ "slug", "sqlx", "syntect", + "tempfile", "test-case", "thiserror 2.0.17", "time", diff --git a/crates/bin/docs_rs_web/Cargo.toml b/crates/bin/docs_rs_web/Cargo.toml index cc46d4d6f..6c9b4a960 100644 --- a/crates/bin/docs_rs_web/Cargo.toml +++ b/crates/bin/docs_rs_web/Cargo.toml @@ -97,5 +97,6 @@ kuchikiki = "0.8" mockito = { workspace = true } opentelemetry_sdk = { workspace = true } pretty_assertions = { workspace = true } +tempfile = { workspace = true } test-case = { workspace = true } walkdir = { workspace = true } diff --git a/crates/bin/docs_rs_web/src/handlers/mod.rs b/crates/bin/docs_rs_web/src/handlers/mod.rs index cdc21d03d..c1ccd760e 100644 --- a/crates/bin/docs_rs_web/src/handlers/mod.rs +++ b/crates/bin/docs_rs_web/src/handlers/mod.rs @@ -118,7 +118,7 @@ pub(crate) async fn build_axum_app( template_data: Arc, ) -> Result { apply_middleware( - routes::build_axum_routes(), + routes::build_axum_routes()?, config, context, Some(template_data), diff --git a/crates/bin/docs_rs_web/src/handlers/statics.rs b/crates/bin/docs_rs_web/src/handlers/statics.rs index abc51cf45..4143273ab 100644 --- a/crates/bin/docs_rs_web/src/handlers/statics.rs +++ b/crates/bin/docs_rs_web/src/handlers/statics.rs @@ -2,6 +2,7 @@ use crate::{ cache::CachePolicy, cache::STATIC_ASSET_CACHE_POLICY, metrics::request_recorder, routes::get_static, }; +use anyhow::{Result, bail}; use axum::{ Router as AxumRouter, extract::{Extension, Request}, @@ -23,22 +24,37 @@ use std::{ use tower_http::services::ServeDir; const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); +const STATIC_DIR_NAME: &str = "static"; +const VENDOR_DIR_NAME: &str = "vendor"; +const STATIC_DIR_NAMES: &[&str] = &[STATIC_DIR_NAME, VENDOR_DIR_NAME]; -/// Find the path to one of the static dirs. +/// Find the root directory for serving our static assets. /// -/// First, check if a directory with the given name exists in the CWD. -/// When it doesn't exist, we fall back to the CARGO_MANIFEST_DIR as root. +/// We have two directories we expect: `vendor` and `static`. /// +/// First we check if they exist in the current working directory: +/// this works +/// * inside the docker container, or +/// * any production deploy, +/// * when running `cargo run` from inside the `docs_rs_web` subcrate. +/// +/// If they don't exist there, we try to find the folders in +/// `CARGO_MANIFEST_DIR`. /// This allows running the server from the project root. -fn static_dir(name: &str) -> PathBuf { - if let Ok(cwd) = env::current_dir() { - let candidate = cwd.join(name); - if candidate.is_dir() { - return candidate; +pub(crate) fn static_root_dir() -> Result { + let manifest_dir = PathBuf::from(MANIFEST_DIR); + for candidate in [env::current_dir()?, manifest_dir] { + if STATIC_DIR_NAMES + .iter() + .all(|name| candidate.join(name).is_dir()) + { + return Ok(candidate); } } - Path::new(MANIFEST_DIR).join(name) + bail!( + "Could not find static root directory containing '{STATIC_DIR_NAME}' and '{VENDOR_DIR_NAME}' folders" + ); } const VENDORED_CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/vendored.css")); @@ -130,7 +146,8 @@ async fn conditional_get( res } -pub(crate) fn build_static_router() -> AxumRouter { +pub(crate) fn build_static_router(root: impl AsRef) -> AxumRouter { + let root = root.as_ref(); AxumRouter::new() .route( "/vendored.css", @@ -154,7 +171,8 @@ pub(crate) fn build_static_router() -> AxumRouter { ) .fallback_service( get_service( - ServeDir::new(static_dir("static")).fallback(ServeDir::new(static_dir("vendor"))), + ServeDir::new(root.join(STATIC_DIR_NAME)) + .fallback(ServeDir::new(root.join(VENDOR_DIR_NAME))), ) .layer(middleware::from_fn(set_needed_static_headers)) .layer(middleware::from_fn(|request, next| async { @@ -167,8 +185,13 @@ pub(crate) fn build_static_router() -> AxumRouter { #[cfg(test)] mod tests { use super::*; - use crate::testing::{ - AxumResponseTestExt, AxumRouterTestExt, TestEnvironmentExt as _, async_wrapper, + use crate::{ + handlers::apply_middleware, + page::TemplateData, + testing::{ + AxumResponseTestExt, AxumRouterTestExt, TestEnvironment, TestEnvironmentExt as _, + async_wrapper, + }, }; use axum::{Router, body::Body}; use docs_rs_headers::compute_etag; @@ -176,12 +199,10 @@ mod tests { HeaderMap, header::{CONTENT_LENGTH, CONTENT_TYPE, ETAG}, }; - use std::fs; + use std::{fs, sync::Arc}; use test_case::test_case; use tower::ServiceExt as _; - const STATIC_SEARCH_PATHS: &[&str] = &["static", "vendor"]; - fn content_length(resp: &Response) -> u64 { resp.headers() .get(CONTENT_LENGTH) @@ -337,8 +358,8 @@ mod tests { async_wrapper(|env| async move { let web = env.web_app().await; - for root in STATIC_SEARCH_PATHS { - let root = static_dir(root); + for root in STATIC_DIR_NAMES { + let root = static_root_dir()?.join(root); for entry in walkdir::WalkDir::new(&root) { let entry = entry?; if !entry.file_type().is_file() { @@ -376,6 +397,48 @@ mod tests { }); } + #[tokio::test(flavor = "multi_thread")] + async fn static_files_should_exist_but_is_locally_deleted() -> Result<()> { + let env = TestEnvironment::new().await?; + + /// build a small axum app with middleware, but just with the static router only. + async fn build_static_app(env: &TestEnvironment, root: impl AsRef) -> Result { + let template_data = Arc::new(TemplateData::new(1).unwrap()); + apply_middleware( + build_static_router(root), + env.config().clone(), + env.context().clone(), + Some(template_data), + ) + .await + } + + const PATH: &str = "/menu.js"; + { + // Sanity check if we have a path that should exist + let web = env.web_app().await; + web.assert_success(&format!("/-/static{PATH}")).await?; + + // and if our static router thing works theoretically + let static_app = build_static_app(&env, &static_root_dir()?).await?; + static_app.assert_success(PATH).await?; + } + + // set up a broken static router. + // The compile-time generated etag map says `menu.js` should exist, + // but in the given root for static files, it's missing. + let tempdir = tempfile::tempdir()?; + let static_app = build_static_app(&env, &tempdir).await?; + + // before bugfix, this would add caching headers, and + // trigger a `debug_assert`. + // The 404 is what we expect. + // `assert_not_found` also asserts if no-caching headers are set. + static_app.assert_not_found(PATH).await?; + + Ok(()) + } + #[test] fn static_mime_types() { async_wrapper(|env| async move { diff --git a/crates/bin/docs_rs_web/src/routes.rs b/crates/bin/docs_rs_web/src/routes.rs index 22e4011ac..8a01ec5c9 100644 --- a/crates/bin/docs_rs_web/src/routes.rs +++ b/crates/bin/docs_rs_web/src/routes.rs @@ -3,10 +3,12 @@ use crate::{ error::AxumNope, handlers::{ about, build_details, builds, crate_details, features, releases, rustdoc, sitemap, source, - statics::build_static_router, status, + statics::{build_static_router, static_root_dir}, + status, }, metrics::request_recorder, }; +use anyhow::Result; use askama::Template; use axum::{ Extension, Router as AxumRouter, @@ -98,7 +100,7 @@ fn cached_permanent_redirect(uri: &str) -> impl IntoResponse { ) } -pub(crate) fn build_axum_routes() -> AxumRouter { +pub(crate) fn build_axum_routes() -> Result { // hint for naming axum routes: // when routes overlap, the route parameters at the same position // have to use the same name: @@ -112,7 +114,7 @@ pub(crate) fn build_axum_routes() -> AxumRouter { // - `/{name}/{version}/settings.html` // - `/{crate}/{version}/{target}` // - AxumRouter::new() + Ok(AxumRouter::new() // Well known resources, robots.txt and favicon.ico support redirection, the sitemap.xml // must live at the site root: // https://developers.google.com/search/reference/robots_txt#handling-http-result-codes @@ -127,7 +129,7 @@ pub(crate) fn build_axum_routes() -> AxumRouter { ) // `.nest` with fallbacks is currently broken, `.nest_service works // https://github.com/tokio-rs/axum/issues/3138 - .nest_service("/-/static", build_static_router()) + .nest_service("/-/static", build_static_router(static_root_dir()?)) .route( "/opensearch.xml", get_static(|| async { cached_permanent_redirect("/-/static/opensearch.xml") }), @@ -345,7 +347,7 @@ pub(crate) fn build_axum_routes() -> AxumRouter { "/{name}/{version}/{target}/{*path}", get_rustdoc(rustdoc::rustdoc_html_server_handler), ) - .fallback(fallback) + .fallback(fallback)) } async fn fallback() -> impl IntoResponse { From 98efec4b63720e9d7c3eb51794498d01e4e83f86 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Fri, 16 Jan 2026 01:30:21 +0100 Subject: [PATCH 4/5] add integration test crate & static file integration test --- Cargo.lock | 13 +++++ Cargo.toml | 2 +- crates/bin/docs_rs_web/src/context.rs | 20 ++++++++ crates/bin/docs_rs_web/src/lib.rs | 2 + crates/bin/docs_rs_web/src/main.rs | 19 +------- integration_tests/Cargo.toml | 13 +++++ integration_tests/src/lib.rs | 1 + integration_tests/tests/web.rs | 70 +++++++++++++++++++++++++++ 8 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 crates/bin/docs_rs_web/src/context.rs create mode 100644 integration_tests/Cargo.toml create mode 100644 integration_tests/src/lib.rs create mode 100644 integration_tests/tests/web.rs diff --git a/Cargo.lock b/Cargo.lock index db3c3f00d..39bdc4d98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2155,6 +2155,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "docs_rs_integration_tests" +version = "0.1.0" +dependencies = [ + "anyhow", + "docs_rs_config", + "docs_rs_context", + "docs_rs_web", + "reqwest 0.13.1", + "tokio", + "tracing", +] + [[package]] name = "docs_rs_logging" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index e18b1d650..e044ac5be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" -members = ["crates/bin/*", "crates/lib/*"] +members = ["crates/bin/*", "crates/lib/*", "integration_tests"] default-members = [ "crates/bin/docs_rs_admin", diff --git a/crates/bin/docs_rs_web/src/context.rs b/crates/bin/docs_rs_web/src/context.rs new file mode 100644 index 000000000..d4d739b73 --- /dev/null +++ b/crates/bin/docs_rs_web/src/context.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use docs_rs_context::Context; +use std::sync::Arc; + +pub async fn build_context() -> Result> { + Ok(Arc::new( + Context::builder() + .with_runtime() + .await? + .with_meter_provider()? + .with_pool() + .await? + .with_build_queue()? + .with_storage() + .await? + .with_registry_api()? + .with_build_limits()? + .build()?, + )) +} diff --git a/crates/bin/docs_rs_web/src/lib.rs b/crates/bin/docs_rs_web/src/lib.rs index e48674658..57e29ddd8 100644 --- a/crates/bin/docs_rs_web/src/lib.rs +++ b/crates/bin/docs_rs_web/src/lib.rs @@ -6,6 +6,7 @@ mod cache; mod config; +mod context; mod error; mod extractors; mod file; @@ -21,6 +22,7 @@ pub(crate) mod testing; mod utils; pub use config::Config; +pub use context::build_context; pub use docs_rs_build_limits::DEFAULT_MAX_TARGETS; pub use docs_rs_utils::{APP_USER_AGENT, BUILD_VERSION, RUSTDOC_STATIC_STORAGE_PREFIX}; pub use font_awesome_as_a_crate::icons; diff --git a/crates/bin/docs_rs_web/src/main.rs b/crates/bin/docs_rs_web/src/main.rs index 7e604ad4f..6172d1a8d 100644 --- a/crates/bin/docs_rs_web/src/main.rs +++ b/crates/bin/docs_rs_web/src/main.rs @@ -1,7 +1,7 @@ use anyhow::Context as _; use clap::Parser; use docs_rs_config::AppConfig as _; -use docs_rs_web::{Config, run_web_server}; +use docs_rs_web::{Config, build_context, run_web_server}; use std::{net::SocketAddr, sync::Arc}; #[derive(Parser)] @@ -20,22 +20,7 @@ async fn main() -> anyhow::Result<()> { let _guard = docs_rs_logging::init().context("error initializing logging")?; let args = Cli::parse(); - - let context = Arc::new( - docs_rs_context::Context::builder() - .with_runtime() - .await? - .with_meter_provider()? - .with_pool() - .await? - .with_build_queue()? - .with_storage() - .await? - .with_registry_api()? - .with_build_limits()? - .build()?, - ); - + let context = build_context().await?; let config = Arc::new(Config::from_environment()?); run_web_server(Some(args.socket_addr), config, context).await?; diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml new file mode 100644 index 000000000..727b71838 --- /dev/null +++ b/integration_tests/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "docs_rs_integration_tests" +version = "0.1.0" +edition = "2024" + +[dev-dependencies] +anyhow = { workspace = true } +docs_rs_config = { path = "../crates/lib/docs_rs_config", features = ["testing"] } +docs_rs_context = { path = "../crates/lib/docs_rs_context", features = ["testing"] } +docs_rs_web = { path = "../crates/bin/docs_rs_web" } +reqwest = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs new file mode 100644 index 000000000..b00f51b08 --- /dev/null +++ b/integration_tests/src/lib.rs @@ -0,0 +1 @@ +// Integration tests live in `integration_tests/tests`. diff --git a/integration_tests/tests/web.rs b/integration_tests/tests/web.rs new file mode 100644 index 000000000..53b3a0958 --- /dev/null +++ b/integration_tests/tests/web.rs @@ -0,0 +1,70 @@ +use anyhow::{Context as _, Result, bail}; +use docs_rs_config::AppConfig; +use docs_rs_context::Context; +use docs_rs_web::{Config, build_context, run_web_server}; +use reqwest::Url; +use std::{sync::Arc, time::Duration}; +use tokio::{ + net::{TcpListener, TcpStream}, + time, +}; +use tracing::debug; + +fn web_config() -> Result> { + Ok(Arc::new(Config::test_config()?)) +} + +/// starts a test webserver. +/// +/// Graceful shutdown is not needed, the test runtime will clean up after +/// the test is finished. +async fn start_web_server(config: Arc, context: Arc) -> Result { + let socket_addr = { + // find free local random port. + let listener = TcpListener::bind("127.0.0.1:0") + .await + .context("error binding socket for web server")?; + + listener.local_addr()? + }; + + tokio::spawn(async move { + run_web_server(Some(socket_addr), config, context) + .await + .expect("error starting web server") + }); + + const WAIT_DURATION: Duration = Duration::from_millis(10); + const WAIT_ATTEMPTS: usize = 20; + + for _attempt in 0..WAIT_ATTEMPTS { + if TcpStream::connect(socket_addr).await.is_ok() { + return Ok(Url::parse(&format!("http://{}/", socket_addr))?); + } + debug!("waiting for webserver to start..."); + time::sleep(WAIT_DURATION).await; + } + + bail!( + "test web server failed to start after {}s", + (WAIT_DURATION * WAIT_ATTEMPTS as u32).as_secs() + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_static_files_from_repo_root() -> Result<()> { + let config = web_config()?; + let context = build_context().await?; + let base_url = start_web_server(config, context).await?; + + assert!( + reqwest::get(base_url.join("/-/static/menu.js")?) + .await? + .error_for_status()? + .text() + .await? + .contains("updateMenuPositionForSubMenu") + ); + + Ok(()) +} From 3c10506f3471aa85a79cb4a64600c99d117fa273 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Fri, 16 Jan 2026 14:07:15 +0100 Subject: [PATCH 5/5] update readme with new commands for the binary crates --- README.md | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 891f66d35..cf101f39c 100644 --- a/README.md +++ b/README.md @@ -66,19 +66,19 @@ docker compose up --wait db s3 # allow downloads from the s3 container to support the /crate/.../download endpoint mcli policy set download docsrs/rust-docs-rs # Setup the database you just created -cargo run -- database migrate +cargo run --bin docs_rs_admin -- database migrate # Update the currently used toolchain to the latest nightly # This also sets up the docs.rs build environment. # This will take a while the first time but will be cached afterwards. -cargo run -- build update-toolchain +cargo run --bin docs_rs_builder -- build update-toolchain # Build a sample crate to make sure it works -cargo run -- build crate regex 1.3.1 +cargo run --bin docs_rs_builder -- build crate regex 1.3.1 # This starts the web server but does not build any crates. # It does not automatically run the migrations, so you need to do that manually (see above). -cargo run -- start-web-server +cargo run --bin docs_rs_web # If you want the server to automatically restart when code or templates change # you can use `cargo-watch`: -cargo watch -x "run -- start-web-server" +cargo watch -x "run --bin docs_rs_web" ``` If you need to store big files in the repository's directory it's recommended to @@ -243,7 +243,7 @@ See `cargo run -- --help` for a full list of commands. ```sh # This command will start web interface of docs.rs on http://localhost:3000 -cargo run -- start-web-server +cargo run --bin docs_rs_webserver start-web-server ``` #### `build` subcommand @@ -252,14 +252,14 @@ cargo run -- start-web-server # Builds and adds it into database # This is the main command to build and add a documentation into docs.rs. # For example, `docker compose run --rm builder-a build crate regex 1.1.6` -cargo run -- build crate +cargo run --bin docs_rs_builder -- build crate # alternatively, within docker-compose containers docker compose run --rm builder-a build crate # Builds every crate on crates.io and adds them into database # (beware: this may take months to finish) -cargo run -- build world +cargo run --bin docs_rs_builder -- build world # Builds a local package you have at and adds it to the database. # The package does not have to be on crates.io. @@ -268,21 +268,18 @@ cargo run -- build world # In certain scenarios it might be necessary to first package the respective # crate by using the `cargo package` command. # See also /docs/build-workspaces.md -cargo run -- build crate --local /path/to/source +cargo run --bin docs_rs_builder -- build crate --local /path/to/source ``` #### `database` subcommand ```sh -# Adds a directory into database to serve with `staticfile` crate. -cargo run -- database add-directory [PREFIX] - # Updates repository stats for crates. # You need to set the DOCSRS_GITHUB_ACCESSTOKEN # environment variable in order to run this command. # Set DOCSRS_GITLAB_ACCESSTOKEN to raise the rate limit for GitLab repositories, # or leave it blank to fetch repositories at a slower rate. -cargo run -- database update-repository-fields +cargo run --bin docs_rs_admin -- database update-repository-fields ``` If you want to explore or edit database manually, you can connect to the database @@ -297,29 +294,21 @@ The database contains a blacklist of crates that should not be built. ```sh # List the crates on the blacklist -cargo run -- database blacklist list +cargo run --bin docs_rs_admin -- database blacklist list # Adds to the blacklist -cargo run -- database blacklist add +cargo run --bin docs_rs_admin -- database blacklist add # Removes from the blacklist -cargo run -- database blacklist remove +cargo run --bin docs_rs_admin -- database blacklist remove ``` If you want to revert to a precise migration, you can run: ```sh -cargo run -- database migrate +cargo run --bin docs_rs_admin -- database migrate ``` -#### `daemon` subcommand - -```sh -# Run a persistent daemon which queues builds and starts a web server. -cargo run -- daemon --registry-watcher=disabled -# Add crates to the queue -cargo run -- queue add -``` ### Updating vendored sources