Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
42c094a7d7 | |||
330c21b12c | |||
9a0d8dfc37 | |||
633873814b | |||
95c54b95c1 | |||
a68a49f6ae | |||
6c7cb9df97 | |||
0a4ec4ecd4 | |||
3ead212fe7 | |||
b001db6558 | |||
5543301140 | |||
028795effa | |||
23303d818a | |||
886caeca1c | |||
6fbd437f34 | |||
13095b0445 | |||
6c8a635e12 | |||
d0ea0c8a68 | |||
6bd843d9b8 | |||
f4206140b3 | |||
7139663544 | |||
0c9ef2abd2 | |||
391bb6faeb | |||
d4248df45a | |||
4b5d962c2c | |||
c93c83f004 | |||
dbbbdad3fa | |||
e82037c23b | |||
ecbc63f522 | |||
315eb514f4 | |||
9139c8f005 | |||
96ded8a0a8 | |||
15692cbb08 | |||
98d80faf3a | |||
a0cb623dc6 | |||
28582e58e0 | |||
e79e1e2634 | |||
097a1affcf | |||
df022f538c | |||
b4327ff28f | |||
38a837bc70 | |||
743fe98d1b | |||
d0e2f21339 | |||
ebe844d305 | |||
c0119ccc97 | |||
6ae96e9b33 | |||
85bd714eb8 | |||
4d63a3cb0b | |||
5d7d06abef | |||
a84885a2ca | |||
ee1de29158 | |||
e48279c6d8 | |||
3df296e542 | |||
a65f8d9d72 | |||
ec04b0a76f | |||
5387265a3e | |||
0379d4bfc0 | |||
7e1b86ace1 | |||
f000dbd27c | |||
3fd245da4a | |||
6f292796a4 | |||
95bc3491ec | |||
9b961032aa |
536
Cargo.lock
generated
536
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
33
Cargo.toml
33
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hoc"
|
name = "hoc"
|
||||||
version = "0.22.4"
|
version = "0.23.0"
|
||||||
authors = ["Valentin Brandl <vbrandl@riseup.net>"]
|
authors = ["Valentin Brandl <vbrandl@riseup.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
@ -16,31 +16,32 @@ name = "hoc"
|
|||||||
actix-rt = "2.7.0"
|
actix-rt = "2.7.0"
|
||||||
actix-web = "4.1.0"
|
actix-web = "4.1.0"
|
||||||
badge = "0.3.0"
|
badge = "0.3.0"
|
||||||
bytes = "1.1.0"
|
bytes = "1.2.1"
|
||||||
config = { version = "0.13.1", features = ["toml"] }
|
config = { version = "0.13.2", features = ["toml"] }
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
futures = "0.3.21"
|
futures = "0.3.23"
|
||||||
git2 = "0.14.4"
|
git2 = "0.15.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
mime = "0.3"
|
||||||
number_prefix = "0.4.0"
|
number_prefix = "0.4.0"
|
||||||
openssl-probe = "0.1.5"
|
openssl-probe = "0.1.5"
|
||||||
reqwest = "0.11.11"
|
reqwest = "0.11.11"
|
||||||
serde = "1.0.137"
|
serde = "1.0.144"
|
||||||
serde_derive = "1.0.136"
|
serde_derive = "1.0.137"
|
||||||
serde_json = "1.0.81"
|
serde_json = "1.0.85"
|
||||||
tracing = "0.1.34"
|
tracing = "0.1.36"
|
||||||
tracing-actix-web = "0.5.1"
|
tracing-actix-web = "0.6.0"
|
||||||
tracing-bunyan-formatter = "0.3.2"
|
tracing-bunyan-formatter = "0.3.3"
|
||||||
tracing-futures = "0.2.5"
|
tracing-futures = "0.2.5"
|
||||||
tracing-log = "0.1.3"
|
tracing-log = "0.1.3"
|
||||||
tracing-subscriber = { version = "0.3.11", features = ["registry", "env-filter"] }
|
tracing-subscriber = { version = "0.3.15", features = ["registry", "env-filter"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
ructe = "0.14.0"
|
ructe = { version = "0.14.2", features = ["mime03"] }
|
||||||
vergen = { version = "7.2.1", default-features = false, features = ["git"] }
|
vergen = { version = "7.3.1", default-features = false, features = ["git"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
awc = "3.0.0"
|
awc = "3.0.0"
|
||||||
ructe = "0.14.0"
|
ructe = "0.14.2"
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
tokio = "1.19.2"
|
tokio = "1.20.1"
|
||||||
|
12
Dockerfile
12
Dockerfile
@ -1,10 +1,13 @@
|
|||||||
FROM ekidd/rust-musl-builder:stable as builder
|
# FROM ekidd/rust-musl-builder:stable as builder
|
||||||
|
FROM clux/muslrust:stable as builder
|
||||||
|
|
||||||
# create new cargo project
|
# create new cargo project
|
||||||
RUN USER=rust cargo init --lib
|
RUN cargo init --lib
|
||||||
|
# RUN USER=rust cargo init --lib
|
||||||
RUN echo 'fn main() { println!("Hello, world!"); }' >> src/main.rs
|
RUN echo 'fn main() { println!("Hello, world!"); }' >> src/main.rs
|
||||||
# copy build config
|
# copy build config
|
||||||
COPY --chown=rust ./Cargo.lock ./Cargo.lock
|
# COPY --chown=rust ./Cargo.lock ./Cargo.lock
|
||||||
|
COPY ./Cargo.lock ./Cargo.lock
|
||||||
COPY ./Cargo.toml ./Cargo.toml
|
COPY ./Cargo.toml ./Cargo.toml
|
||||||
# HACK: remove build-dependencies so we have at least some caching
|
# HACK: remove build-dependencies so we have at least some caching
|
||||||
RUN head -n $(($(grep -n "\[build-dependencies\]" Cargo.toml | cut -f1 -d:) - 1)) Cargo.toml | sed '/build.rs/d' > \
|
RUN head -n $(($(grep -n "\[build-dependencies\]" Cargo.toml | cut -f1 -d:) - 1)) Cargo.toml | sed '/build.rs/d' > \
|
||||||
@ -38,6 +41,7 @@ USER hoc
|
|||||||
# FROM scratch
|
# FROM scratch
|
||||||
# COPY --from=linuxkit/ca-certificates:v0.7 / /
|
# COPY --from=linuxkit/ca-certificates:v0.7 / /
|
||||||
|
|
||||||
COPY --from=builder /home/rust/src/target/x86_64-unknown-linux-musl/release/hoc .
|
# COPY --from=builder /home/rust/src/target/x86_64-unknown-linux-musl/release/hoc .
|
||||||
|
COPY --from=builder /volume/target/x86_64-unknown-linux-musl/release/hoc .
|
||||||
|
|
||||||
ENTRYPOINT ["/home/hoc/hoc"]
|
ENTRYPOINT ["/home/hoc/hoc"]
|
||||||
|
5
build.rs
5
build.rs
@ -8,5 +8,8 @@ fn main() -> Result<(), RucteError> {
|
|||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
*config.git_mut().sha_kind_mut() = ShaKind::Short;
|
*config.git_mut().sha_kind_mut() = ShaKind::Short;
|
||||||
vergen(config).expect("Unable to generate static repo info");
|
vergen(config).expect("Unable to generate static repo info");
|
||||||
Ructe::from_env()?.compile_templates("templates")
|
let mut ructe = Ructe::from_env()?;
|
||||||
|
let mut statics = ructe.statics()?;
|
||||||
|
statics.add_files("static")?;
|
||||||
|
ructe.compile_templates("templates")
|
||||||
}
|
}
|
||||||
|
37
src/count.rs
37
src/count.rs
@ -1,27 +1,30 @@
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use std::{fs::read_dir, path::Path, result::Result as StdResult};
|
use std::{
|
||||||
|
fs::{read_dir, ReadDir},
|
||||||
|
iter::once,
|
||||||
|
path::Path,
|
||||||
|
result::Result as StdResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The on disk layout for served repos is `<service>/<user>/<repo>`
|
||||||
|
/// so to get the amount of repos, we just have to count everything
|
||||||
|
/// in `*/*/*` to get the count.
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub(crate) fn count_repositories<P>(repo_path: P) -> Result<usize>
|
pub fn count_repositories<P>(repo_path: P) -> Result<usize>
|
||||||
where
|
where
|
||||||
P: AsRef<Path> + std::fmt::Debug,
|
P: AsRef<Path> + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
trace!("Counting repositories");
|
trace!("Counting repositories");
|
||||||
std::fs::create_dir_all(&repo_path)?;
|
std::fs::create_dir_all(&repo_path)?;
|
||||||
Ok(read_dir(repo_path)?
|
Ok(once(read_dir(repo_path)?)
|
||||||
.filter_map(StdResult::ok)
|
.flat_map(sub_directories)
|
||||||
.filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
|
.flat_map(sub_directories)
|
||||||
.map(|entry| read_dir(entry.path()))
|
.flat_map(sub_directories)
|
||||||
.filter_map(StdResult::ok)
|
|
||||||
.flat_map(|dir| {
|
|
||||||
dir.filter_map(StdResult::ok)
|
|
||||||
.filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
|
|
||||||
})
|
|
||||||
.map(|entry| read_dir(entry.path()))
|
|
||||||
.filter_map(StdResult::ok)
|
|
||||||
.flat_map(|dir| {
|
|
||||||
dir.filter_map(StdResult::ok)
|
|
||||||
.filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
|
|
||||||
})
|
|
||||||
.count())
|
.count())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sub_directories(dir: ReadDir) -> impl Iterator<Item = ReadDir> {
|
||||||
|
dir.filter_map(StdResult::ok)
|
||||||
|
.filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
|
||||||
|
.filter_map(|entry| read_dir(entry.path()).ok())
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ use std::fmt;
|
|||||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Error {
|
pub enum Error {
|
||||||
Badge(String),
|
Badge(String),
|
||||||
Client(reqwest::Error),
|
Client(reqwest::Error),
|
||||||
Git(git2::Error),
|
Git(git2::Error),
|
||||||
|
51
src/lib.rs
51
src/lib.rs
@ -11,7 +11,7 @@ extern crate tracing;
|
|||||||
|
|
||||||
mod cache;
|
mod cache;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
mod count;
|
pub mod count;
|
||||||
mod error;
|
mod error;
|
||||||
mod service;
|
mod service;
|
||||||
mod statics;
|
mod statics;
|
||||||
@ -23,8 +23,8 @@ use crate::{
|
|||||||
config::Settings,
|
config::Settings,
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
service::{Bitbucket, FormService, GitHub, Gitlab, Service, Sourcehut},
|
service::{Bitbucket, FormService, GitHub, Gitlab, Service, Sourcehut},
|
||||||
statics::{CLIENT, CSS, FAVICON, VERSION_INFO},
|
statics::{CLIENT, VERSION_INFO},
|
||||||
template::RepoInfo,
|
template::{RepoGeneratorInfo, RepoInfo},
|
||||||
};
|
};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::Server,
|
dev::Server,
|
||||||
@ -46,6 +46,7 @@ use std::{
|
|||||||
sync::atomic::Ordering,
|
sync::atomic::Ordering,
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
use templates::statics::{self as template_statics, StaticFile};
|
||||||
use tracing::Instrument;
|
use tracing::Instrument;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
||||||
@ -55,6 +56,7 @@ struct GeneratorForm<'a> {
|
|||||||
service: FormService,
|
service: FormService,
|
||||||
user: Cow<'a, str>,
|
user: Cow<'a, str>,
|
||||||
repo: Cow<'a, str>,
|
repo: Cow<'a, str>,
|
||||||
|
branch: Option<Cow<'a, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -437,16 +439,23 @@ async fn generate(
|
|||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
repo_count: web::Data<AtomicUsize>,
|
repo_count: web::Data<AtomicUsize>,
|
||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
let repo = format!("{}/{}", params.user, params.repo);
|
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
|
let repo_info = RepoGeneratorInfo {
|
||||||
|
service: params.service,
|
||||||
|
user: ¶ms.user,
|
||||||
|
repo: ¶ms.repo,
|
||||||
|
branch: params
|
||||||
|
.branch
|
||||||
|
.as_deref()
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.unwrap_or("master"),
|
||||||
|
};
|
||||||
templates::generate(
|
templates::generate(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
VERSION_INFO,
|
VERSION_INFO,
|
||||||
repo_count.load(Ordering::Relaxed),
|
repo_count.load(Ordering::Relaxed),
|
||||||
&state.settings.base_url,
|
&state.settings.base_url,
|
||||||
params.service.url(),
|
&repo_info,
|
||||||
params.service.service(),
|
|
||||||
&repo,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().content_type("text/html").body(buf))
|
Ok(HttpResponse::Ok().content_type("text/html").body(buf))
|
||||||
@ -462,14 +471,32 @@ async fn async_p404(repo_count: web::Data<AtomicUsize>) -> Result<HttpResponse>
|
|||||||
p404(repo_count)
|
p404(repo_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/tacit-css.min.css")]
|
/// A duration to add to current time for a far expires header.
|
||||||
async fn css() -> HttpResponse {
|
static FAR: Duration = Duration::from_secs(180 * 24 * 60 * 60);
|
||||||
HttpResponse::Ok().content_type("text/css").body(CSS)
|
|
||||||
|
#[get("/static/{filename}")]
|
||||||
|
async fn static_file(
|
||||||
|
path: web::Path<String>,
|
||||||
|
repo_count: web::Data<AtomicUsize>,
|
||||||
|
) -> Result<HttpResponse> {
|
||||||
|
StaticFile::get(&path)
|
||||||
|
.map(|data| {
|
||||||
|
let far_expires = SystemTime::now() + FAR;
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.insert_header(Expires(far_expires.into()))
|
||||||
|
.content_type(data.mime.clone())
|
||||||
|
.body(data.content)
|
||||||
|
})
|
||||||
|
.map(Result::Ok)
|
||||||
|
.unwrap_or_else(|| p404(repo_count))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/favicon.ico")]
|
#[get("/favicon.ico")]
|
||||||
async fn favicon32() -> HttpResponse {
|
async fn favicon32() -> HttpResponse {
|
||||||
HttpResponse::Ok().content_type("image/png").body(FAVICON)
|
let data = &template_statics::favicon32_png;
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.content_type(data.mime.clone())
|
||||||
|
.body(data.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_server(listener: TcpListener, settings: Settings) -> std::io::Result<Server> {
|
async fn start_server(listener: TcpListener, settings: Settings) -> std::io::Result<Server> {
|
||||||
@ -486,7 +513,7 @@ async fn start_server(listener: TcpListener, settings: Settings) -> std::io::Res
|
|||||||
.wrap(middleware::NormalizePath::new(TrailingSlash::Trim))
|
.wrap(middleware::NormalizePath::new(TrailingSlash::Trim))
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(health_check)
|
.service(health_check)
|
||||||
.service(css)
|
.service(static_file)
|
||||||
.service(favicon32)
|
.service(favicon32)
|
||||||
.service(generate)
|
.service(generate)
|
||||||
.default_service(web::to(async_p404));
|
.default_service(web::to(async_p404));
|
||||||
|
@ -28,8 +28,8 @@ pub(crate) trait Service: Sized + 'static {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize, Clone, Copy)]
|
||||||
pub(crate) enum FormService {
|
pub enum FormService {
|
||||||
#[serde(rename = "github")]
|
#[serde(rename = "github")]
|
||||||
GitHub,
|
GitHub,
|
||||||
#[serde(rename = "gitlab")]
|
#[serde(rename = "gitlab")]
|
||||||
|
@ -7,8 +7,6 @@ pub(crate) const VERSION_INFO: VersionInfo = VersionInfo {
|
|||||||
commit: env!("VERGEN_GIT_SHA_SHORT"),
|
commit: env!("VERGEN_GIT_SHA_SHORT"),
|
||||||
version: env!("CARGO_PKG_VERSION"),
|
version: env!("CARGO_PKG_VERSION"),
|
||||||
};
|
};
|
||||||
pub(crate) const CSS: &str = include_str!("../static/tacit-css.min.css");
|
|
||||||
pub(crate) const FAVICON: &[u8] = include_bytes!("../static/favicon32.png");
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub(crate) static ref CLIENT: reqwest::Client = reqwest::Client::new();
|
pub(crate) static ref CLIENT: reqwest::Client = reqwest::Client::new();
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use crate::service::FormService;
|
||||||
|
|
||||||
pub struct RepoInfo<'a> {
|
pub struct RepoInfo<'a> {
|
||||||
pub commit_url: &'a str,
|
pub commit_url: &'a str,
|
||||||
pub commits: u64,
|
pub commits: u64,
|
||||||
@ -9,3 +11,10 @@ pub struct RepoInfo<'a> {
|
|||||||
pub url: &'a str,
|
pub url: &'a str,
|
||||||
pub branch: &'a str,
|
pub branch: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RepoGeneratorInfo<'a> {
|
||||||
|
pub service: FormService,
|
||||||
|
pub user: &'a str,
|
||||||
|
pub repo: &'a str,
|
||||||
|
pub branch: &'a str,
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
@use super::statics::*;
|
||||||
@use crate::statics::VersionInfo;
|
@use crate::statics::VersionInfo;
|
||||||
|
|
||||||
@(title: &str, header: &str, content: Content, version_info: VersionInfo, repo_count: usize)
|
@(title: &str, header: &str, content: Content, version_info: VersionInfo, repo_count: usize)
|
||||||
@ -9,7 +10,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="keywords" content="Hits-of-Code, GitHub, Badge" />
|
<meta name="keywords" content="Hits-of-Code, GitHub, Badge" />
|
||||||
<meta name="description" content="Hits-of-Code Badges for Git repositories" />
|
<meta name="description" content="Hits-of-Code Badges for Git repositories" />
|
||||||
<link rel="stylesheet" href="/tacit-css.min.css" />
|
<link rel="stylesheet" href="/static/@tacit_css_min_css.name" />
|
||||||
<title>@title</title>
|
<title>@title</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
@use super::base;
|
@use super::base;
|
||||||
@use crate::statics::VersionInfo;
|
@use crate::{statics::VersionInfo, template::RepoGeneratorInfo};
|
||||||
|
|
||||||
@(version_info: VersionInfo, repo_count: usize, base_url: &str, url: &str, service: &str, path: &str)
|
@(version_info: VersionInfo, repo_count: usize, base_url: &str, repo_info: &RepoGeneratorInfo)
|
||||||
|
|
||||||
@:base("Hits-of-Code Badges", "Badge Generator", {
|
@:base("Hits-of-Code Badges", "Badge Generator", {
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Here is the markdown for the badge for <a href="https://@url/@path">@url/@path</a>
|
Here is the markdown for the badge for <a href="https://@repo_info.service.url()/@repo_info.user/@repo_info.repo">@repo_info.user/@repo_info.repo</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
[](@base_url/@service/@path/view)
|
[/@repo_info.user/@repo_info.repo?branch=@repo_info.branch)](@base_url/@repo_info.service.service()/@repo_info.user/@repo_info.repo/view?branch=@repo_info.branch)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@ -18,6 +18,6 @@ It will be rendered like this
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
<a href="@base_url/@service/@path/view"><img src="@base_url/@service/@path" alt="example badge" /></a>
|
<a href="@base_url/@repo_info.service.service()/@repo_info.user/@repo_info.repo/view?branch=@repo_info.branch"><img src="@base_url/@repo_info.service.service()/@repo_info.user/@repo_info.repo?branch=@repo_info.branch" alt="example badge" /></a>
|
||||||
</pre>
|
</pre>
|
||||||
}, version_info, repo_count)
|
}, version_info, repo_count)
|
||||||
|
@ -80,6 +80,8 @@ the lines of
|
|||||||
<input name="user" id="user" type="text" placeholder="user" />
|
<input name="user" id="user" type="text" placeholder="user" />
|
||||||
<label>/</label>
|
<label>/</label>
|
||||||
<input name="repo" id="repo" type="text" placeholder="repository" />
|
<input name="repo" id="repo" type="text" placeholder="repository" />
|
||||||
|
<label>:</label>
|
||||||
|
<input name="branch" id="branch" type="text" placeholder="branch (defaults to `master`)" />
|
||||||
<button type="submit">Generate</button>
|
<button type="submit">Generate</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
66
tests/count.rs
Normal file
66
tests/count.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use hoc::count::count_repositories;
|
||||||
|
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_repos() {
|
||||||
|
let repos = TempDir::new().unwrap();
|
||||||
|
assert_eq!(0, count_repositories(&repos).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_repos_for_provider() {
|
||||||
|
let repos = TempDir::new().unwrap();
|
||||||
|
let _provider = TempDir::new_in(&repos).unwrap();
|
||||||
|
assert_eq!(0, count_repositories(&repos).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_repos_for_owner() {
|
||||||
|
let repos = TempDir::new().unwrap();
|
||||||
|
let provider = TempDir::new_in(&repos).unwrap();
|
||||||
|
let _owner = TempDir::new_in(&provider).unwrap();
|
||||||
|
assert_eq!(0, count_repositories(&repos).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_repo_for_owner() {
|
||||||
|
let repos = TempDir::new().unwrap();
|
||||||
|
let provider = TempDir::new_in(&repos).unwrap();
|
||||||
|
let owner = TempDir::new_in(&provider).unwrap();
|
||||||
|
let _repo = TempDir::new_in(&owner).unwrap();
|
||||||
|
assert_eq!(1, count_repositories(&repos).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_repos_for_owner() {
|
||||||
|
let repos = TempDir::new().unwrap();
|
||||||
|
let provider = TempDir::new_in(&repos).unwrap();
|
||||||
|
let owner = TempDir::new_in(&provider).unwrap();
|
||||||
|
let _repo1 = TempDir::new_in(&owner).unwrap();
|
||||||
|
let _repo2 = TempDir::new_in(&owner).unwrap();
|
||||||
|
assert_eq!(2, count_repositories(&repos).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_repos_for_two_providers() {
|
||||||
|
let repos = TempDir::new().unwrap();
|
||||||
|
let provider1 = TempDir::new_in(&repos).unwrap();
|
||||||
|
let owner1 = TempDir::new_in(&provider1).unwrap();
|
||||||
|
let _repo1 = TempDir::new_in(&owner1).unwrap();
|
||||||
|
let provider2 = TempDir::new_in(&repos).unwrap();
|
||||||
|
let owner2 = TempDir::new_in(&provider2).unwrap();
|
||||||
|
let _repo2 = TempDir::new_in(&owner2).unwrap();
|
||||||
|
assert_eq!(2, count_repositories(&repos).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_subdirs_in_one_repo() {
|
||||||
|
let repos = TempDir::new().unwrap();
|
||||||
|
let provider = TempDir::new_in(&repos).unwrap();
|
||||||
|
let owner = TempDir::new_in(&provider).unwrap();
|
||||||
|
let repo = TempDir::new_in(&owner).unwrap();
|
||||||
|
let _subdir1 = TempDir::new_in(&repo).unwrap();
|
||||||
|
let _subdir2 = TempDir::new_in(&repo).unwrap();
|
||||||
|
assert_eq!(1, count_repositories(&repos).unwrap())
|
||||||
|
}
|
@ -14,18 +14,3 @@ async fn favicon() {
|
|||||||
|
|
||||||
assert!(response.status().is_success());
|
assert!(response.status().is_success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn tacit_css() {
|
|
||||||
let test_app = util::spawn_app().await;
|
|
||||||
|
|
||||||
let client = awc::Client::default();
|
|
||||||
|
|
||||||
let response = client
|
|
||||||
.get(&format!("{}/tacit-css.min.css", test_app.address))
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.expect("Failed to execute request");
|
|
||||||
|
|
||||||
assert!(response.status().is_success());
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user