collin/anvil · b056637e
style: apply nightly rustfmt import grouping
Collin Richards · 2026-06-10 08:12 UTC · b056637e21e56c9da7054fad8493e0a5e58111e1 · parent 0c9a5673 · browse files
modifiedcrates/anvil-ci/src/lib.rs+21 −5
| ⋯ 13 unchanged lines | |||
| 14 | 14 | //! pids/memory/cpu caps plus a wall-clock timeout and an optional image | |
| 15 | 15 | //! allowlist ([`anvil_core::config::CiConfig`]). | |
| 16 | 16 | ||
| 17 | - | use anvil_core::ci::{self, Pipeline}; | |
| 17 | + | use anvil_core::ci::{ | |
| 18 | + | self, | |
| 19 | + | Pipeline, | |
| 20 | + | }; | |
| 18 | 21 | use anvil_core::config::CiConfig; | |
| 19 | - | use anvil_core::{App, repos, storage, users}; | |
| 20 | - | use anvil_git::browse::{self, TreeFile}; | |
| 22 | + | use anvil_core::{ | |
| 23 | + | App, | |
| 24 | + | repos, | |
| 25 | + | storage, | |
| 26 | + | users, | |
| 27 | + | }; | |
| 28 | + | use anvil_git::browse::{ | |
| 29 | + | self, | |
| 30 | + | TreeFile, | |
| 31 | + | }; | |
| 21 | 32 | use bollard::Docker; | |
| 22 | 33 | use bollard::container::{ | |
| 23 | - | Config, CreateContainerOptions, LogsOptions, RemoveContainerOptions, StartContainerOptions, | |
| 24 | - | UploadToContainerOptions, WaitContainerOptions, | |
| 34 | + | Config, | |
| 35 | + | CreateContainerOptions, | |
| 36 | + | LogsOptions, | |
| 37 | + | RemoveContainerOptions, | |
| 38 | + | StartContainerOptions, | |
| 39 | + | UploadToContainerOptions, | |
| 40 | + | WaitContainerOptions, | |
| 25 | 41 | }; | |
| 26 | 42 | use bollard::image::CreateImageOptions; | |
| 27 | 43 | use bollard::models::HostConfig; | |
| ⋯ 300 unchanged lines | |||
modifiedcrates/anvil-core/src/access.rs+4 −1
| ⋯ 3 unchanged lines | |||
| 4 | 4 | //! a repo is owned by one user; private repos are visible only to their owner; | |
| 5 | 5 | //! writes (push) and settings changes are owner-only. Admins may do anything. | |
| 6 | 6 | ||
| 7 | - | use crate::models::{Repository, User}; | |
| 7 | + | use crate::models::{ | |
| 8 | + | Repository, | |
| 9 | + | User, | |
| 10 | + | }; | |
| 8 | 11 | ||
| 9 | 12 | /// Whether `viewer` may read (browse / clone) `repo`. | |
| 10 | 13 | pub fn can_read(repo: &Repository, viewer: Option<&User>) -> bool { | |
| ⋯ 10 unchanged lines | |||
modifiedcrates/anvil-core/src/ci.rs+4 −1
| ⋯ 2 unchanged lines | |||
| 3 | 3 | ||
| 4 | 4 | use serde::Deserialize; | |
| 5 | 5 | ||
| 6 | - | use crate::error::{Error, Result}; | |
| 6 | + | use crate::error::{ | |
| 7 | + | Error, | |
| 8 | + | Result, | |
| 9 | + | }; | |
| 7 | 10 | use crate::models::CiRun; | |
| 8 | 11 | ||
| 9 | 12 | /// Run status values stored in [`CiRun::status`]. | |
| ⋯ 230 unchanged lines | |||
modifiedcrates/anvil-core/src/config.rs+12 −3
| 1 | 1 | //! Server configuration: loaded from a TOML file with sensible defaults. | |
| 2 | 2 | ||
| 3 | - | use std::path::{Path, PathBuf}; | |
| 3 | + | use std::path::{ | |
| 4 | + | Path, | |
| 5 | + | PathBuf, | |
| 6 | + | }; | |
| 4 | 7 | ||
| 5 | - | use serde::{Deserialize, Serialize}; | |
| 8 | + | use serde::{ | |
| 9 | + | Deserialize, | |
| 10 | + | Serialize, | |
| 11 | + | }; | |
| 6 | 12 | ||
| 7 | - | use crate::error::{Error, Result}; | |
| 13 | + | use crate::error::{ | |
| 14 | + | Error, | |
| 15 | + | Result, | |
| 16 | + | }; | |
| 8 | 17 | ||
| 9 | 18 | /// Top-level anvil configuration. | |
| 10 | 19 | /// | |
| ⋯ 260 unchanged lines | |||
modifiedcrates/anvil-core/src/db.rs+7 −1
| ⋯ 2 unchanged lines | |||
| 3 | 3 | use std::path::Path; | |
| 4 | 4 | ||
| 5 | 5 | use crate::error::Result; | |
| 6 | - | use crate::models::{CiRun, Repository, Session, SshKey, User}; | |
| 6 | + | use crate::models::{ | |
| 7 | + | CiRun, | |
| 8 | + | Repository, | |
| 9 | + | Session, | |
| 10 | + | SshKey, | |
| 11 | + | User, | |
| 12 | + | }; | |
| 7 | 13 | ||
| 8 | 14 | /// Open (creating if necessary) the SQLite database at `path`, creating the | |
| 9 | 15 | /// schema on first run. | |
| ⋯ 22 unchanged lines | |||
modifiedcrates/anvil-core/src/lib.rs+19 −4
| ⋯ 17 unchanged lines | |||
| 18 | 18 | pub mod users; | |
| 19 | 19 | ||
| 20 | 20 | pub use config::Config; | |
| 21 | - | pub use error::{Error, Result}; | |
| 22 | - | pub use models::{CiRun, Repository, Session, SshKey, User}; | |
| 21 | + | pub use error::{ | |
| 22 | + | Error, | |
| 23 | + | Result, | |
| 24 | + | }; | |
| 25 | + | pub use models::{ | |
| 26 | + | CiRun, | |
| 27 | + | Repository, | |
| 28 | + | Session, | |
| 29 | + | SshKey, | |
| 30 | + | User, | |
| 31 | + | }; | |
| 23 | 32 | ||
| 24 | 33 | /// Current Unix time in seconds, for `created_at` columns. | |
| 25 | 34 | pub(crate) fn now() -> i64 { | |
| ⋯ 48 unchanged lines | |||
| 74 | 83 | /// session)`, hex-encoded. Stable for a session's lifetime, unguessable | |
| 75 | 84 | /// without the server secret, and requires no extra storage. | |
| 76 | 85 | pub fn csrf_token(&self, session_token: &str) -> String { | |
| 77 | - | use hmac::{Hmac, Mac}; | |
| 86 | + | use hmac::{ | |
| 87 | + | Hmac, | |
| 88 | + | Mac, | |
| 89 | + | }; | |
| 78 | 90 | let mut mac = Hmac::<sha2::Sha256>::new_from_slice(self.csrf_secret.as_slice()) | |
| 79 | 91 | .expect("HMAC accepts any key length"); | |
| 80 | 92 | mac.update(session_token.as_bytes()); | |
| ⋯ 7 unchanged lines | |||
| 88 | 100 | ||
| 89 | 101 | /// Load the persistent CSRF secret, generating and saving it on first run. | |
| 90 | 102 | fn load_or_create_csrf_secret(data_dir: &std::path::Path) -> Result<[u8; 32]> { | |
| 91 | - | use argon2::password_hash::rand_core::{OsRng, RngCore}; | |
| 103 | + | use argon2::password_hash::rand_core::{ | |
| 104 | + | OsRng, | |
| 105 | + | RngCore, | |
| 106 | + | }; | |
| 92 | 107 | ||
| 93 | 108 | let path = data_dir.join("csrf_secret"); | |
| 94 | 109 | if path.exists() { | |
| ⋯ 16 unchanged lines | |||
modifiedcrates/anvil-core/src/repos.rs+8 −2
| ⋯ 1 unchanged line | |||
| 2 | 2 | ||
| 3 | 3 | use std::path::Path; | |
| 4 | 4 | ||
| 5 | - | use crate::error::{Error, Result}; | |
| 6 | - | use crate::models::{Repository, User}; | |
| 5 | + | use crate::error::{ | |
| 6 | + | Error, | |
| 7 | + | Result, | |
| 8 | + | }; | |
| 9 | + | use crate::models::{ | |
| 10 | + | Repository, | |
| 11 | + | User, | |
| 12 | + | }; | |
| 7 | 13 | use crate::storage; | |
| 8 | 14 | ||
| 9 | 15 | /// A repository joined with its owner's username, for listing pages. | |
| ⋯ 141 unchanged lines | |||
modifiedcrates/anvil-core/src/sessions.rs+8 −2
| 1 | 1 | //! Web login sessions: opaque random tokens stored server-side and referenced | |
| 2 | 2 | //! by a cookie. | |
| 3 | 3 | ||
| 4 | - | use argon2::password_hash::rand_core::{OsRng, RngCore}; | |
| 4 | + | use argon2::password_hash::rand_core::{ | |
| 5 | + | OsRng, | |
| 6 | + | RngCore, | |
| 7 | + | }; | |
| 5 | 8 | ||
| 6 | 9 | use crate::error::Result; | |
| 7 | - | use crate::models::{Session, User}; | |
| 10 | + | use crate::models::{ | |
| 11 | + | Session, | |
| 12 | + | User, | |
| 13 | + | }; | |
| 8 | 14 | ||
| 9 | 15 | /// Default session lifetime: 30 days. | |
| 10 | 16 | const SESSION_TTL_SECS: i64 = 60 * 60 * 24 * 30; | |
| ⋯ 63 unchanged lines | |||
modifiedcrates/anvil-core/src/ssh_keys.rs+8 −2
| 1 | 1 | //! SSH public keys: parsing, registration, and lookup for git-over-SSH | |
| 2 | 2 | //! authentication. | |
| 3 | 3 | ||
| 4 | - | use ssh_key::{HashAlg, PublicKey}; | |
| 4 | + | use ssh_key::{ | |
| 5 | + | HashAlg, | |
| 6 | + | PublicKey, | |
| 7 | + | }; | |
| 5 | 8 | ||
| 6 | - | use crate::error::{Error, Result}; | |
| 9 | + | use crate::error::{ | |
| 10 | + | Error, | |
| 11 | + | Result, | |
| 12 | + | }; | |
| 7 | 13 | use crate::models::SshKey; | |
| 8 | 14 | ||
| 9 | 15 | /// Parse an OpenSSH public-key line (`ssh-ed25519 AAAA… comment`) into its | |
| ⋯ 83 unchanged lines | |||
modifiedcrates/anvil-core/src/storage.rs+14 −3
| ⋯ 4 unchanged lines | |||
| 5 | 5 | //! between forge identities and filesystem paths, plus creation/opening via | |
| 6 | 6 | //! `gix`. No database access happens here. | |
| 7 | 7 | ||
| 8 | - | use std::path::{Path, PathBuf}; | |
| 8 | + | use std::path::{ | |
| 9 | + | Path, | |
| 10 | + | PathBuf, | |
| 11 | + | }; | |
| 9 | 12 | ||
| 10 | - | use crate::error::{Error, Result}; | |
| 13 | + | use crate::error::{ | |
| 14 | + | Error, | |
| 15 | + | Result, | |
| 16 | + | }; | |
| 11 | 17 | ||
| 12 | 18 | /// Compute the on-disk path of a bare repository. | |
| 13 | 19 | pub fn repo_path(repositories_dir: &Path, owner: &str, name: &str) -> PathBuf { | |
| ⋯ 33 unchanged lines | |||
| 47 | 53 | /// Point `HEAD` at `refs/heads/<branch>` as a symbolic reference. | |
| 48 | 54 | fn set_head_branch(repo: &gix::Repository, branch: &str) -> Result<()> { | |
| 49 | 55 | use gix::refs::Target; | |
| 50 | - | use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit}; | |
| 56 | + | use gix::refs::transaction::{ | |
| 57 | + | Change, | |
| 58 | + | LogChange, | |
| 59 | + | PreviousValue, | |
| 60 | + | RefEdit, | |
| 61 | + | }; | |
| 51 | 62 | ||
| 52 | 63 | let target_name: gix::refs::FullName = format!("refs/heads/{branch}") | |
| 53 | 64 | .try_into() | |
| ⋯ 23 unchanged lines | |||
modifiedcrates/anvil-core/src/users.rs+10 −2
| ⋯ 1 unchanged line | |||
| 2 | 2 | ||
| 3 | 3 | use argon2::Argon2; | |
| 4 | 4 | use argon2::password_hash::rand_core::OsRng; | |
| 5 | - | use argon2::password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}; | |
| 5 | + | use argon2::password_hash::{ | |
| 6 | + | PasswordHash, | |
| 7 | + | PasswordHasher, | |
| 8 | + | PasswordVerifier, | |
| 9 | + | SaltString, | |
| 10 | + | }; | |
| 6 | 11 | ||
| 7 | - | use crate::error::{Error, Result}; | |
| 12 | + | use crate::error::{ | |
| 13 | + | Error, | |
| 14 | + | Result, | |
| 15 | + | }; | |
| 8 | 16 | use crate::models::User; | |
| 9 | 17 | ||
| 10 | 18 | /// Usernames reserved for system use — they collide with (or could be confused | |
| ⋯ 94 unchanged lines | |||
modifiedcrates/anvil-git/src/browse.rs+8 −2
| ⋯ 2 unchanged lines | |||
| 3 | 3 | //! These helpers back the web UI. Each opens the bare repo by path; that is | |
| 4 | 4 | //! cheap enough at our scale and keeps the API stateless. | |
| 5 | 5 | ||
| 6 | - | use std::collections::{BTreeMap, BTreeSet}; | |
| 6 | + | use std::collections::{ | |
| 7 | + | BTreeMap, | |
| 8 | + | BTreeSet, | |
| 9 | + | }; | |
| 7 | 10 | use std::fmt::Display; | |
| 8 | 11 | use std::path::Path; | |
| 9 | 12 | ||
| 10 | - | use crate::error::{Error, Result}; | |
| 13 | + | use crate::error::{ | |
| 14 | + | Error, | |
| 15 | + | Result, | |
| 16 | + | }; | |
| 11 | 17 | ||
| 12 | 18 | /// Summary of a repository's refs and state, for the repo landing page. | |
| 13 | 19 | pub struct Overview { | |
| ⋯ 485 unchanged lines | |||
modifiedcrates/anvil-git/src/lib.rs+8 −2
| ⋯ 18 unchanged lines | |||
| 19 | 19 | pub mod ssh; | |
| 20 | 20 | pub mod trigger; | |
| 21 | 21 | ||
| 22 | - | pub use error::{Error, Result}; | |
| 23 | - | pub use smart_http::{Service, UploadPack}; | |
| 22 | + | pub use error::{ | |
| 23 | + | Error, | |
| 24 | + | Result, | |
| 25 | + | }; | |
| 26 | + | pub use smart_http::{ | |
| 27 | + | Service, | |
| 28 | + | UploadPack, | |
| 29 | + | }; | |
modifiedcrates/anvil-git/src/smart_http.rs+5 −1
| ⋯ 13 unchanged lines | |||
| 14 | 14 | use std::pin::Pin; | |
| 15 | 15 | ||
| 16 | 16 | use gitserver_core::backend::GitBackend; | |
| 17 | - | use gitserver_core::{pack, pktline, protocol_v2}; | |
| 17 | + | use gitserver_core::{ | |
| 18 | + | pack, | |
| 19 | + | pktline, | |
| 20 | + | protocol_v2, | |
| 21 | + | }; | |
| 18 | 22 | use tokio::io::AsyncRead; | |
| 19 | 23 | ||
| 20 | 24 | use crate::error::Result; | |
| ⋯ 131 unchanged lines | |||
modifiedcrates/anvil-git/src/ssh.rs+11 −2
| ⋯ 11 unchanged lines | |||
| 12 | 12 | use std::path::Path; | |
| 13 | 13 | ||
| 14 | 14 | use gitserver_core::backend::GitBackend; | |
| 15 | - | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; | |
| 15 | + | use tokio::io::{ | |
| 16 | + | AsyncRead, | |
| 17 | + | AsyncReadExt, | |
| 18 | + | AsyncWrite, | |
| 19 | + | AsyncWriteExt, | |
| 20 | + | }; | |
| 16 | 21 | ||
| 17 | 22 | use crate::error::Result; | |
| 18 | - | use crate::smart_http::{self, Service, UploadPack}; | |
| 23 | + | use crate::smart_http::{ | |
| 24 | + | self, | |
| 25 | + | Service, | |
| 26 | + | UploadPack, | |
| 27 | + | }; | |
| 19 | 28 | ||
| 20 | 29 | /// Parse an SSH `exec` command such as `git-upload-pack '/alice/hello.git'`, | |
| 21 | 30 | /// returning the service and the (leading-slash-trimmed) repository path. | |
| ⋯ 198 unchanged lines | |||
modifiedcrates/anvil-ssh/src/lib.rs+33 −6
| ⋯ 6 unchanged lines | |||
| 7 | 7 | //! The SSH bind address is configurable (`[ssh] listen` in the config). | |
| 8 | 8 | ||
| 9 | 9 | use std::net::SocketAddr; | |
| 10 | - | use std::path::{Path, PathBuf}; | |
| 10 | + | use std::path::{ | |
| 11 | + | Path, | |
| 12 | + | PathBuf, | |
| 13 | + | }; | |
| 11 | 14 | use std::sync::Arc; | |
| 12 | 15 | use std::time::Duration; | |
| 13 | 16 | ||
| 14 | - | use anvil_core::{App, Error as CoreError, Result as CoreResult, access, repos, users}; | |
| 17 | + | use anvil_core::{ | |
| 18 | + | App, | |
| 19 | + | Error as CoreError, | |
| 20 | + | Result as CoreResult, | |
| 21 | + | access, | |
| 22 | + | repos, | |
| 23 | + | users, | |
| 24 | + | }; | |
| 15 | 25 | use anvil_git::ssh as git_ssh; | |
| 16 | - | use russh::keys::ssh_key::{HashAlg, LineEnding, PublicKey}; | |
| 17 | - | use russh::keys::{Algorithm, PrivateKey}; | |
| 18 | - | use russh::server::{self, Auth, Handler, Msg, Server as _, Session}; | |
| 19 | - | use russh::{Channel, ChannelId}; | |
| 26 | + | use russh::keys::ssh_key::{ | |
| 27 | + | HashAlg, | |
| 28 | + | LineEnding, | |
| 29 | + | PublicKey, | |
| 30 | + | }; | |
| 31 | + | use russh::keys::{ | |
| 32 | + | Algorithm, | |
| 33 | + | PrivateKey, | |
| 34 | + | }; | |
| 35 | + | use russh::server::{ | |
| 36 | + | self, | |
| 37 | + | Auth, | |
| 38 | + | Handler, | |
| 39 | + | Msg, | |
| 40 | + | Server as _, | |
| 41 | + | Session, | |
| 42 | + | }; | |
| 43 | + | use russh::{ | |
| 44 | + | Channel, | |
| 45 | + | ChannelId, | |
| 46 | + | }; | |
| 20 | 47 | use tokio::net::TcpListener; | |
| 21 | 48 | ||
| 22 | 49 | /// Bind to the configured SSH address and serve git over SSH until shutdown. | |
| ⋯ 244 unchanged lines | |||
modifiedcrates/anvil-web/src/auth.rs+29 −6
| ⋯ 2 unchanged lines | |||
| 3 | 3 | ||
| 4 | 4 | use std::convert::Infallible; | |
| 5 | 5 | ||
| 6 | - | use anvil_core::{App, User, sessions, users}; | |
| 6 | + | use anvil_core::{ | |
| 7 | + | App, | |
| 8 | + | User, | |
| 9 | + | sessions, | |
| 10 | + | users, | |
| 11 | + | }; | |
| 7 | 12 | use axum::{ | |
| 8 | 13 | Form, | |
| 9 | - | extract::{FromRequestParts, Request, State}, | |
| 10 | - | http::{StatusCode, request::Parts}, | |
| 14 | + | extract::{ | |
| 15 | + | FromRequestParts, | |
| 16 | + | Request, | |
| 17 | + | State, | |
| 18 | + | }, | |
| 19 | + | http::{ | |
| 20 | + | StatusCode, | |
| 21 | + | request::Parts, | |
| 22 | + | }, | |
| 11 | 23 | middleware::Next, | |
| 12 | - | response::{IntoResponse, Redirect, Response}, | |
| 24 | + | response::{ | |
| 25 | + | IntoResponse, | |
| 26 | + | Redirect, | |
| 27 | + | Response, | |
| 28 | + | }, | |
| 13 | 29 | }; | |
| 14 | - | use axum_extra::extract::cookie::{Cookie, CookieJar, SameSite}; | |
| 15 | - | use maud::{Markup, html}; | |
| 30 | + | use axum_extra::extract::cookie::{ | |
| 31 | + | Cookie, | |
| 32 | + | CookieJar, | |
| 33 | + | SameSite, | |
| 34 | + | }; | |
| 35 | + | use maud::{ | |
| 36 | + | Markup, | |
| 37 | + | html, | |
| 38 | + | }; | |
| 16 | 39 | ||
| 17 | 40 | use crate::ui::layout; | |
| 18 | 41 | ||
| ⋯ 202 unchanged lines | |||
modifiedcrates/anvil-web/src/git_http.rs+34 −7
| ⋯ 10 unchanged lines | |||
| 11 | 11 | use std::collections::HashMap; | |
| 12 | 12 | use std::path::PathBuf; | |
| 13 | 13 | ||
| 14 | - | use anvil_core::{App, Repository, access, repos, users}; | |
| 15 | - | use anvil_git::smart_http::{self, Service, UploadPack}; | |
| 14 | + | use anvil_core::{ | |
| 15 | + | App, | |
| 16 | + | Repository, | |
| 17 | + | access, | |
| 18 | + | repos, | |
| 19 | + | users, | |
| 20 | + | }; | |
| 21 | + | use anvil_git::smart_http::{ | |
| 22 | + | self, | |
| 23 | + | Service, | |
| 24 | + | UploadPack, | |
| 25 | + | }; | |
| 16 | 26 | use axum::{ | |
| 17 | 27 | Router, | |
| 18 | - | body::{Body, Bytes}, | |
| 19 | - | extract::{Path, Query, State}, | |
| 20 | - | http::{HeaderMap, StatusCode, header}, | |
| 21 | - | response::{IntoResponse, Response}, | |
| 22 | - | routing::{get, post}, | |
| 28 | + | body::{ | |
| 29 | + | Body, | |
| 30 | + | Bytes, | |
| 31 | + | }, | |
| 32 | + | extract::{ | |
| 33 | + | Path, | |
| 34 | + | Query, | |
| 35 | + | State, | |
| 36 | + | }, | |
| 37 | + | http::{ | |
| 38 | + | HeaderMap, | |
| 39 | + | StatusCode, | |
| 40 | + | header, | |
| 41 | + | }, | |
| 42 | + | response::{ | |
| 43 | + | IntoResponse, | |
| 44 | + | Response, | |
| 45 | + | }, | |
| 46 | + | routing::{ | |
| 47 | + | get, | |
| 48 | + | post, | |
| 49 | + | }, | |
| 23 | 50 | }; | |
| 24 | 51 | use tokio_util::io::ReaderStream; | |
| 25 | 52 | ||
| ⋯ 178 unchanged lines | |||
modifiedcrates/anvil-web/src/lib.rs+8 −2
| ⋯ 7 unchanged lines | |||
| 8 | 8 | // pattern is intentional and the responses are never hot-path allocated en masse. | |
| 9 | 9 | #![allow(clippy::result_large_err)] | |
| 10 | 10 | ||
| 11 | - | use anvil_core::{App, Result}; | |
| 11 | + | use anvil_core::{ | |
| 12 | + | App, | |
| 13 | + | Result, | |
| 14 | + | }; | |
| 12 | 15 | use axum::{ | |
| 13 | 16 | Router, | |
| 14 | - | routing::{get, post}, | |
| 17 | + | routing::{ | |
| 18 | + | get, | |
| 19 | + | post, | |
| 20 | + | }, | |
| 15 | 21 | }; | |
| 16 | 22 | ||
| 17 | 23 | pub mod auth; | |
| ⋯ 37 unchanged lines | |||
modifiedcrates/anvil-web/src/pages.rs+19 −4
| ⋯ 15 unchanged lines | |||
| 16 | 16 | use anvil_git::browse; | |
| 17 | 17 | use axum::{ | |
| 18 | 18 | Router, | |
| 19 | - | extract::{Path, State}, | |
| 19 | + | extract::{ | |
| 20 | + | Path, | |
| 21 | + | State, | |
| 22 | + | }, | |
| 20 | 23 | http::header, | |
| 21 | - | response::{IntoResponse, Redirect, Response}, | |
| 24 | + | response::{ | |
| 25 | + | IntoResponse, | |
| 26 | + | Redirect, | |
| 27 | + | Response, | |
| 28 | + | }, | |
| 22 | 29 | routing::get, | |
| 23 | 30 | }; | |
| 24 | - | use maud::{Markup, html}; | |
| 31 | + | use maud::{ | |
| 32 | + | Markup, | |
| 33 | + | html, | |
| 34 | + | }; | |
| 25 | 35 | ||
| 26 | 36 | use crate::auth::CurrentUser; | |
| 27 | - | use crate::ui::{layout, not_found, resolve_repo, server_error}; | |
| 37 | + | use crate::ui::{ | |
| 38 | + | layout, | |
| 39 | + | not_found, | |
| 40 | + | resolve_repo, | |
| 41 | + | server_error, | |
| 42 | + | }; | |
| 28 | 43 | ||
| 29 | 44 | /// The branch pages are served from. | |
| 30 | 45 | pub const PAGES_REF: &str = "pages"; | |
| ⋯ 139 unchanged lines | |||
modifiedcrates/anvil-web/src/ui.rs+77 −16
| ⋯ 1 unchanged line | |||
| 2 | 2 | //! blob viewing. Pages are plain SSR and work without JavaScript; htmx-based | |
| 3 | 3 | //! progressive enhancement is a follow-up. | |
| 4 | 4 | ||
| 5 | - | use std::collections::{BTreeMap, HashMap}; | |
| 5 | + | use std::collections::{ | |
| 6 | + | BTreeMap, | |
| 7 | + | HashMap, | |
| 8 | + | }; | |
| 6 | 9 | use std::path::PathBuf; | |
| 7 | - | use std::sync::{Arc, Mutex, OnceLock}; | |
| 10 | + | use std::sync::{ | |
| 11 | + | Arc, | |
| 12 | + | Mutex, | |
| 13 | + | OnceLock, | |
| 14 | + | }; | |
| 8 | 15 | ||
| 9 | - | use anvil_core::{App, CiRun, Repository, SshKey, User, access, ci, repos, ssh_keys, users}; | |
| 10 | - | use anvil_git::browse::{self, ChangeKind, FileChange}; | |
| 16 | + | use anvil_core::{ | |
| 17 | + | App, | |
| 18 | + | CiRun, | |
| 19 | + | Repository, | |
| 20 | + | SshKey, | |
| 21 | + | User, | |
| 22 | + | access, | |
| 23 | + | ci, | |
| 24 | + | repos, | |
| 25 | + | ssh_keys, | |
| 26 | + | users, | |
| 27 | + | }; | |
| 28 | + | use anvil_git::browse::{ | |
| 29 | + | self, | |
| 30 | + | ChangeKind, | |
| 31 | + | FileChange, | |
| 32 | + | }; | |
| 11 | 33 | use axum::{ | |
| 12 | - | Form, Router, | |
| 13 | - | extract::{Path, Query, State}, | |
| 14 | - | http::{StatusCode, header}, | |
| 15 | - | response::{IntoResponse, Redirect, Response}, | |
| 16 | - | routing::{get, post}, | |
| 34 | + | Form, | |
| 35 | + | Router, | |
| 36 | + | extract::{ | |
| 37 | + | Path, | |
| 38 | + | Query, | |
| 39 | + | State, | |
| 40 | + | }, | |
| 41 | + | http::{ | |
| 42 | + | StatusCode, | |
| 43 | + | header, | |
| 44 | + | }, | |
| 45 | + | response::{ | |
| 46 | + | IntoResponse, | |
| 47 | + | Redirect, | |
| 48 | + | Response, | |
| 49 | + | }, | |
| 50 | + | routing::{ | |
| 51 | + | get, | |
| 52 | + | post, | |
| 53 | + | }, | |
| 54 | + | }; | |
| 55 | + | use maud::{ | |
| 56 | + | DOCTYPE, | |
| 57 | + | Markup, | |
| 58 | + | PreEscaped, | |
| 59 | + | html, | |
| 60 | + | }; | |
| 61 | + | use similar::{ | |
| 62 | + | ChangeTag, | |
| 63 | + | TextDiff, | |
| 17 | 64 | }; | |
| 18 | - | use maud::{DOCTYPE, Markup, PreEscaped, html}; | |
| 19 | - | use similar::{ChangeTag, TextDiff}; | |
| 20 | 65 | use syntect::easy::HighlightLines; | |
| 21 | - | use syntect::highlighting::{Theme, ThemeSet}; | |
| 22 | - | use syntect::html::{IncludeBackground, styled_line_to_highlighted_html}; | |
| 66 | + | use syntect::highlighting::{ | |
| 67 | + | Theme, | |
| 68 | + | ThemeSet, | |
| 69 | + | }; | |
| 70 | + | use syntect::html::{ | |
| 71 | + | IncludeBackground, | |
| 72 | + | styled_line_to_highlighted_html, | |
| 73 | + | }; | |
| 23 | 74 | use syntect::parsing::SyntaxSet; | |
| 24 | 75 | use time::OffsetDateTime; | |
| 25 | 76 | ||
| 26 | - | use crate::auth::{CSRF_FIELD, Csrf, CurrentUser, verify_csrf}; | |
| 77 | + | use crate::auth::{ | |
| 78 | + | CSRF_FIELD, | |
| 79 | + | Csrf, | |
| 80 | + | CurrentUser, | |
| 81 | + | verify_csrf, | |
| 82 | + | }; | |
| 27 | 83 | ||
| 28 | 84 | const STYLE: &str = r#" | |
| 29 | 85 | :root { --fg:#1f2328; --muted:#656d76; --bg:#fff; --border:#d0d7de; --accent:#0969da; --code-bg:#f6f8fa; } | |
| ⋯ 100 unchanged lines | |||
| 130 | 186 | ||
| 131 | 187 | /// Clipboard icon for the clone "copy" button. | |
| 132 | 188 | const CLIPBOARD_SVG: &str = r#"<svg viewBox="0 0 16 16" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M10 1.5H6a.5.5 0 0 0-.5.5v1H4A1.5 1.5 0 0 0 2.5 4.5v9A1.5 1.5 0 0 0 4 15h8a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 12 3h-1.5V2a.5.5 0 0 0-.5-.5zm-3.5 1h3v1h-3v-1zM4 4.5h8v9H4v-9z"/></svg>"#; | |
| 133 | - | ||
| 134 | 189 | /// Delegated handlers for the clone widget: protocol toggle + copy-to-clipboard. | |
| 135 | 190 | /// Registered once on `document`, so it survives htmx body swaps. | |
| 136 | 191 | const CLONE_JS: &str = r#" | |
| ⋯ 793 unchanged lines | |||
| 930 | 985 | /// source is emitted as escaped literal text, and `javascript:`/`data:`-style | |
| 931 | 986 | /// link and image destinations are dropped. | |
| 932 | 987 | fn render_markdown(text: &str) -> Markup { | |
| 933 | - | use pulldown_cmark::{Event, Options, Parser, Tag, html}; | |
| 988 | + | use pulldown_cmark::{ | |
| 989 | + | Event, | |
| 990 | + | Options, | |
| 991 | + | Parser, | |
| 992 | + | Tag, | |
| 993 | + | html, | |
| 994 | + | }; | |
| 934 | 995 | ||
| 935 | 996 | fn safe_url(dest: &str) -> bool { | |
| 936 | 997 | let d = dest.trim().to_ascii_lowercase(); | |
| ⋯ 568 unchanged lines | |||
modifiedcrates/anvil/src/main.rs+15 −3
| 1 | 1 | //! `anvild` — the anvil git forge daemon and admin CLI. | |
| 2 | 2 | ||
| 3 | - | use anvil_core::{App, Config, repos, ssh_keys, users}; | |
| 4 | - | use anyhow::{Context, Result}; | |
| 5 | - | use clap::{Parser, Subcommand}; | |
| 3 | + | use anvil_core::{ | |
| 4 | + | App, | |
| 5 | + | Config, | |
| 6 | + | repos, | |
| 7 | + | ssh_keys, | |
| 8 | + | users, | |
| 9 | + | }; | |
| 10 | + | use anyhow::{ | |
| 11 | + | Context, | |
| 12 | + | Result, | |
| 13 | + | }; | |
| 14 | + | use clap::{ | |
| 15 | + | Parser, | |
| 16 | + | Subcommand, | |
| 17 | + | }; | |
| 6 | 18 | ||
| 7 | 19 | #[derive(Parser)] | |
| 8 | 20 | #[command(name = "anvild", version, about = "anvil git forge")] | |
| ⋯ 201 unchanged lines | |||
modifiedvendor/gitserver-core/src/backend.rs+13 −3
| 1 | 1 | use std::path::PathBuf; | |
| 2 | 2 | use std::pin::Pin; | |
| 3 | 3 | use std::sync::Arc; | |
| 4 | - | use std::sync::atomic::{AtomicBool, Ordering}; | |
| 5 | - | use std::task::{Context, Poll}; | |
| 4 | + | use std::sync::atomic::{ | |
| 5 | + | AtomicBool, | |
| 6 | + | Ordering, | |
| 7 | + | }; | |
| 8 | + | use std::task::{ | |
| 9 | + | Context, | |
| 10 | + | Poll, | |
| 11 | + | }; | |
| 6 | 12 | ||
| 7 | 13 | use tokio::io::AsyncRead; | |
| 8 | - | use tokio::time::{Duration, Sleep, sleep}; | |
| 14 | + | use tokio::time::{ | |
| 15 | + | Duration, | |
| 16 | + | Sleep, | |
| 17 | + | sleep, | |
| 18 | + | }; | |
| 9 | 19 | use tokio_util::io::SyncIoBridge; | |
| 10 | 20 | ||
| 11 | 21 | use crate::error::Result; | |
| ⋯ 230 unchanged lines | |||
modifiedvendor/gitserver-core/src/discovery.rs+13 −3
| 1 | 1 | use std::fs; | |
| 2 | - | use std::path::{Path, PathBuf}; | |
| 2 | + | use std::path::{ | |
| 3 | + | Path, | |
| 4 | + | PathBuf, | |
| 5 | + | }; | |
| 3 | 6 | ||
| 4 | 7 | use serde::Serialize; | |
| 5 | 8 | ||
| 6 | - | pub use crate::dynamic_registry::{DynamicRepoRegistry, MutableRepoRegistry, RepoResolver}; | |
| 7 | - | use crate::error::{Error, Result}; | |
| 9 | + | pub use crate::dynamic_registry::{ | |
| 10 | + | DynamicRepoRegistry, | |
| 11 | + | MutableRepoRegistry, | |
| 12 | + | RepoResolver, | |
| 13 | + | }; | |
| 14 | + | use crate::error::{ | |
| 15 | + | Error, | |
| 16 | + | Result, | |
| 17 | + | }; | |
| 8 | 18 | ||
| 9 | 19 | const DEFAULT_GIT_DESCRIPTION: &str = | |
| 10 | 20 | "Unnamed repository; edit this file 'description' to name the repository."; | |
| ⋯ 285 unchanged lines | |||
modifiedvendor/gitserver-core/src/dynamic_registry.rs+16 −4
| ⋯ 3 unchanged lines | |||
| 4 | 4 | // | |
| 5 | 5 | // Copyright (c) 2026 WJQSERVER | |
| 6 | 6 | ||
| 7 | - | use std::path::{Path, PathBuf}; | |
| 8 | - | use std::sync::{Arc, RwLock}; | |
| 7 | + | use std::path::{ | |
| 8 | + | Path, | |
| 9 | + | PathBuf, | |
| 10 | + | }; | |
| 11 | + | use std::sync::{ | |
| 12 | + | Arc, | |
| 13 | + | RwLock, | |
| 14 | + | }; | |
| 9 | 15 | ||
| 10 | - | use crate::discovery::{RepoInfo, RepoStore}; | |
| 11 | - | use crate::error::{Error, Result}; | |
| 16 | + | use crate::discovery::{ | |
| 17 | + | RepoInfo, | |
| 18 | + | RepoStore, | |
| 19 | + | }; | |
| 20 | + | use crate::error::{ | |
| 21 | + | Error, | |
| 22 | + | Result, | |
| 23 | + | }; | |
| 12 | 24 | ||
| 13 | 25 | pub trait RepoResolver: Send + Sync { | |
| 14 | 26 | fn resolve(&self, relative: &str) -> Result<RepoInfo>; | |
| ⋯ 249 unchanged lines | |||
modifiedvendor/gitserver-core/src/pack.rs+12 −3
| ⋯ 1 unchanged line | |||
| 2 | 2 | use std::path::Path; | |
| 3 | 3 | ||
| 4 | 4 | use bytes::Bytes; | |
| 5 | - | use sha1::{Digest, Sha1}; | |
| 5 | + | use sha1::{ | |
| 6 | + | Digest, | |
| 7 | + | Sha1, | |
| 8 | + | }; | |
| 6 | 9 | use tokio::io::AsyncRead; | |
| 7 | 10 | use tokio_util::io::StreamReader; | |
| 8 | 11 | ||
| 9 | - | use crate::error::{Error, Result}; | |
| 12 | + | use crate::error::{ | |
| 13 | + | Error, | |
| 14 | + | Result, | |
| 15 | + | }; | |
| 10 | 16 | use crate::pktline; | |
| 11 | 17 | ||
| 12 | 18 | #[derive(Debug, Clone, Default)] | |
| ⋯ 637 unchanged lines | |||
| 650 | 656 | ||
| 651 | 657 | #[cfg(test)] | |
| 652 | 658 | mod tests { | |
| 653 | - | use std::path::{Path, PathBuf}; | |
| 659 | + | use std::path::{ | |
| 660 | + | Path, | |
| 661 | + | PathBuf, | |
| 662 | + | }; | |
| 654 | 663 | use std::process::Command; | |
| 655 | 664 | ||
| 656 | 665 | use tempfile::TempDir; | |
| ⋯ 183 unchanged lines | |||
modifiedvendor/gitserver-core/src/path.rs+9 −2
| 1 | - | use std::path::{Component, Path, PathBuf}; | |
| 1 | + | use std::path::{ | |
| 2 | + | Component, | |
| 3 | + | Path, | |
| 4 | + | PathBuf, | |
| 5 | + | }; | |
| 2 | 6 | ||
| 3 | - | use crate::error::{Error, Result}; | |
| 7 | + | use crate::error::{ | |
| 8 | + | Error, | |
| 9 | + | Result, | |
| 10 | + | }; | |
| 4 | 11 | ||
| 5 | 12 | /// Normalize a path by resolving `.` and `..` components lexically, | |
| 6 | 13 | /// without touching the filesystem. | |
| ⋯ 90 unchanged lines | |||
modifiedvendor/gitserver-core/src/protocol_v2.rs+8 −2
| ⋯ 8 unchanged lines | |||
| 9 | 9 | use std::io::Cursor; | |
| 10 | 10 | use std::path::Path; | |
| 11 | 11 | ||
| 12 | - | use crate::error::{Error, Result}; | |
| 12 | + | use crate::error::{ | |
| 13 | + | Error, | |
| 14 | + | Result, | |
| 15 | + | }; | |
| 13 | 16 | use crate::pack::UploadPackRequest; | |
| 14 | 17 | use crate::pktline; | |
| 15 | 18 | ||
| ⋯ 566 unchanged lines | |||
| 582 | 585 | request: &crate::pack::UploadPackRequest, | |
| 583 | 586 | depth: usize, | |
| 584 | 587 | ) -> Result<DepthState> { | |
| 585 | - | use std::collections::{HashSet, VecDeque}; | |
| 588 | + | use std::collections::{ | |
| 589 | + | HashSet, | |
| 590 | + | VecDeque, | |
| 591 | + | }; | |
| 586 | 592 | ||
| 587 | 593 | let mut queue = VecDeque::new(); | |
| 588 | 594 | let mut seen = HashSet::new(); | |
| ⋯ 158 unchanged lines | |||
modifiedvendor/gitserver-core/src/receive_pack.rs+16 −3
| ⋯ 3 unchanged lines | |||
| 4 | 4 | // | |
| 5 | 5 | // Copyright (c) 2026 WJQSERVER | |
| 6 | 6 | ||
| 7 | - | use std::io::{BufRead, BufReader, Read}; | |
| 7 | + | use std::io::{ | |
| 8 | + | BufRead, | |
| 9 | + | BufReader, | |
| 10 | + | Read, | |
| 11 | + | }; | |
| 8 | 12 | use std::path::Path; | |
| 9 | 13 | use std::sync::atomic::AtomicBool; | |
| 10 | 14 | ||
| ⋯ 1 unchanged line | |||
| 12 | 16 | use gix::prelude::ObjectIdExt; | |
| 13 | 17 | use gix::progress::Discard; | |
| 14 | 18 | use gix::refs::Target; | |
| 15 | - | use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog}; | |
| 19 | + | use gix::refs::transaction::{ | |
| 20 | + | Change, | |
| 21 | + | LogChange, | |
| 22 | + | PreviousValue, | |
| 23 | + | RefEdit, | |
| 24 | + | RefLog, | |
| 25 | + | }; | |
| 16 | 26 | ||
| 17 | - | use crate::error::{Error, Result}; | |
| 27 | + | use crate::error::{ | |
| 28 | + | Error, | |
| 29 | + | Result, | |
| 30 | + | }; | |
| 18 | 31 | use crate::pktline; | |
| 19 | 32 | ||
| 20 | 33 | const ZERO_ID: &str = "0000000000000000000000000000000000000000"; | |
| ⋯ 642 unchanged lines | |||
modifiedvendor/gitserver-core/src/refs.rs+4 −1
| ⋯ 73 unchanged lines | |||
| 74 | 74 | ||
| 75 | 75 | #[cfg(test)] | |
| 76 | 76 | mod tests { | |
| 77 | - | use std::path::{Path, PathBuf}; | |
| 77 | + | use std::path::{ | |
| 78 | + | Path, | |
| 79 | + | PathBuf, | |
| 80 | + | }; | |
| 78 | 81 | use std::process::Command; | |
| 79 | 82 | ||
| 80 | 83 | use tempfile::TempDir; | |
| ⋯ 140 unchanged lines | |||