Compare commits
28 Commits
feature/st
...
v0.14.5
Author | SHA1 | Date | |
---|---|---|---|
9f8b781f7b | |||
175b7c828b | |||
002119324f | |||
df78b6f1e5 | |||
e1bff0d280 | |||
4c9454aa9e | |||
e9e57495a6 | |||
b6505f6a37 | |||
a2463bf657 | |||
53cb73cd9b | |||
423a3aa0b0 | |||
7a2c6b6f06 | |||
ffb306a7a8 | |||
cef2ae2299 | |||
b91de72d19 | |||
fd08489587 | |||
ce0d6041ea | |||
3af60a82ce | |||
bcdf7db549 | |||
9b2f1f4ebb | |||
84e47237de | |||
909f6585b5 | |||
b48d7f1492 | |||
f1e9d1806f | |||
8c62d01f3c | |||
26a5025a32 | |||
6f931ce46f | |||
2d46592c4a |
23
.github/workflows/audit.yml
vendored
23
.github/workflows/audit.yml
vendored
@ -4,27 +4,24 @@ on:
|
||||
- cron: '0 1 * * *'
|
||||
push:
|
||||
paths:
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
security_audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
- name: Cache cargo registry, index and build directory
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /usr/share/rust/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: /usr/share/rust/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-index
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: audit-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
|
20
.github/workflows/nix-build.yml
vendored
20
.github/workflows/nix-build.yml
vendored
@ -1,20 +0,0 @@
|
||||
name: "Nix Build"
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: cachix/install-nix-action@v8
|
||||
- name: Cache nix store
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: /nix
|
||||
key: ${{ runner.os }}-nix-store
|
||||
- uses: cachix/cachix-action@v5
|
||||
with:
|
||||
name: hitsofcode
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
attributes: package
|
76
.github/workflows/release.yml
vendored
Normal file
76
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publishing for ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
rust: [stable]
|
||||
include:
|
||||
- os: macos-latest
|
||||
artifact_prefix: macos
|
||||
target: x86_64-apple-darwin
|
||||
binary_postfix: ""
|
||||
- os: ubuntu-latest
|
||||
artifact_prefix: linux
|
||||
target: x86_64-unknown-linux-gnu
|
||||
binary_postfix: ""
|
||||
- os: windows-latest
|
||||
artifact_prefix: windows
|
||||
target: x86_64-pc-windows-msvc
|
||||
binary_postfix: ".exe"
|
||||
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
|
||||
- name: Cache cargo registry, index and build directory
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: release-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cargo build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
toolchain: ${{ matrix.rust }}
|
||||
args: --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Packaging final binary
|
||||
shell: bash
|
||||
run: |
|
||||
cd target/${{ matrix.target }}/release
|
||||
strip hoc${{ matrix.binary_postfix }}
|
||||
tar czvf hoc-${{ matrix.artifact_prefix }}.tar.gz hoc${{ matrix.binary_postfix }}
|
||||
|
||||
if [[ ${{ runner.os }} == 'Windows' ]]; then
|
||||
certutil -hashfile hoc-${{ matrix.artifact_prefix }}.tar.gz sha256 | grep -E [A-Fa-f0-9]{64} > hoc-${{ matrix.artifact_prefix }}.sha256
|
||||
else
|
||||
shasum -a 256 hoc-${{ matrix.artifact_prefix }}.tar.gz > hoc-${{ matrix.artifact_prefix }}.sha256
|
||||
fi
|
||||
|
||||
- name: Releasing assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
target/${{ matrix.target }}/release/hoc-${{ matrix.artifact_prefix }}.tar.gz
|
||||
target/${{ matrix.target }}/release/hoc-${{ matrix.artifact_prefix }}.sha256
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
91
.github/workflows/rust.yml
vendored
91
.github/workflows/rust.yml
vendored
@ -1,41 +1,30 @@
|
||||
name: Rust
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Linting and Formatting Checks
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: Install rustfmt
|
||||
run: rustup component add rustfmt
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
- name: Cache cargo registry, index and build directory
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: rustfmt-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Check Formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
@ -43,8 +32,29 @@ jobs:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Install clippy
|
||||
run: rustup component add clippy
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: Cache cargo registry, index and build directory
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: clippy-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Clippy Linting
|
||||
uses: actions-rs/cargo@v1
|
||||
@ -52,8 +62,9 @@ jobs:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
|
||||
test:
|
||||
name: Run Tests
|
||||
name: Test Suite
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
@ -62,31 +73,23 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
- name: Cache cargo registry, index and build directory
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: test-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Run Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
|
17
.travis.yml
17
.travis.yml
@ -1,17 +0,0 @@
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
cache:
|
||||
- cargo
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_failure: always
|
605
Cargo.lock
generated
605
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
19
Cargo.toml
19
Cargo.toml
@ -1,33 +1,32 @@
|
||||
[package]
|
||||
name = "hoc"
|
||||
version = "0.14.3"
|
||||
version = "0.14.5"
|
||||
authors = ["Valentin Brandl <vbrandl@riseup.net>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-rt = "1.1.1"
|
||||
actix-slog = "0.2.1"
|
||||
actix-web = "3.1.0"
|
||||
actix-web = "3.2.0"
|
||||
badge = "0.3.0"
|
||||
bytes = "0.6.0"
|
||||
futures = "0.3.7"
|
||||
futures = "0.3.8"
|
||||
git2 = "0.13.12"
|
||||
lazy_static = "1.4.0"
|
||||
number_prefix = "0.4.0"
|
||||
openssl-probe = "0.1.2"
|
||||
reqwest = "0.10.8"
|
||||
reqwest = "0.10.9"
|
||||
serde = "1.0.117"
|
||||
serde_derive = "1.0.103"
|
||||
serde_json = "1.0.59"
|
||||
slog = "2.5.2"
|
||||
slog-async = "2.5.0"
|
||||
slog-atomic = "3.0.0"
|
||||
slog-term = "2.6.0"
|
||||
structopt = "0.3.20"
|
||||
tracing = "0.1.21"
|
||||
tracing-subscriber = "0.2.15"
|
||||
tracing-actix-web = "0.2.1"
|
||||
tracing-futures = "0.2.4"
|
||||
|
||||
[build-dependencies]
|
||||
ructe = "0.12.0"
|
||||
ructe = "0.13.0"
|
||||
vergen = "3.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -3,7 +3,6 @@
|
||||
[](https://hitsofcode.com/view/github/vbrandl/hoc)
|
||||
[](https://drone.vbrandl.net/vbrandl/hoc)
|
||||
[](https://gitlab.com/vbrandl/hoc/pipelines)
|
||||
[](https://travis-ci.org/vbrandl/hoc)
|
||||
[](https://deps.rs/repo/github/vbrandl/hoc)
|
||||
|
||||
Small webservice, that returns a badge of the Hits-of-Code of a git repository, as described by [Yegor
|
||||
|
19
src/cache.rs
19
src/cache.rs
@ -8,6 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
/// Enum to indicate the state of the cache
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum CacheState<'a> {
|
||||
/// Current head and cached head are the same
|
||||
Current {
|
||||
@ -26,11 +27,13 @@ pub(crate) enum CacheState<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CacheState<'a> {
|
||||
#[instrument]
|
||||
pub(crate) fn read_from_file(
|
||||
path: impl AsRef<Path>,
|
||||
path: impl AsRef<Path> + std::fmt::Debug,
|
||||
branch: &str,
|
||||
head: &str,
|
||||
) -> Result<CacheState<'a>> {
|
||||
trace!("Reading cache");
|
||||
if path.as_ref().exists() {
|
||||
let cache: Cache = serde_json::from_reader(BufReader::new(File::open(path)?))?;
|
||||
Ok(cache
|
||||
@ -38,6 +41,7 @@ impl<'a> CacheState<'a> {
|
||||
.get(branch)
|
||||
.map(|c| {
|
||||
if c.head == head {
|
||||
trace!("Cache is up to date");
|
||||
CacheState::Current {
|
||||
count: c.count,
|
||||
commits: c.commits,
|
||||
@ -45,6 +49,7 @@ impl<'a> CacheState<'a> {
|
||||
cache: cache.clone(),
|
||||
}
|
||||
} else {
|
||||
trace!("Cache is out of date");
|
||||
CacheState::Old {
|
||||
head: c.head.to_string(),
|
||||
// TODO: get rid of clone
|
||||
@ -59,6 +64,7 @@ impl<'a> CacheState<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub(crate) fn calculate_new_cache(
|
||||
self,
|
||||
count: u64,
|
||||
@ -66,6 +72,7 @@ impl<'a> CacheState<'a> {
|
||||
head: Cow<'a, str>,
|
||||
branch: &'a str,
|
||||
) -> Cache<'a> {
|
||||
trace!("Calculating new cache");
|
||||
match self {
|
||||
CacheState::Old { mut cache, .. } => {
|
||||
if let Some(mut cache) = cache.entries.get_mut(branch) {
|
||||
@ -77,6 +84,7 @@ impl<'a> CacheState<'a> {
|
||||
}
|
||||
CacheState::Current { cache, .. } => cache,
|
||||
CacheState::NoneForBranch(mut cache) => {
|
||||
trace!("Creating new cache for branch");
|
||||
cache.entries.insert(
|
||||
branch.into(),
|
||||
CacheEntry {
|
||||
@ -88,6 +96,7 @@ impl<'a> CacheState<'a> {
|
||||
cache
|
||||
}
|
||||
CacheState::No => {
|
||||
trace!("Creating new cache file");
|
||||
let mut entries = HashMap::with_capacity(1);
|
||||
entries.insert(
|
||||
branch.into(),
|
||||
@ -103,12 +112,12 @@ impl<'a> CacheState<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(crate) struct Cache<'a> {
|
||||
pub entries: HashMap<Cow<'a, str>, CacheEntry<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(crate) struct CacheEntry<'a> {
|
||||
/// HEAD commit ref
|
||||
pub head: Cow<'a, str>,
|
||||
@ -119,7 +128,9 @@ pub(crate) struct CacheEntry<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Cache<'a> {
|
||||
pub(crate) fn write_to_file(&self, path: impl AsRef<Path>) -> Result<()> {
|
||||
#[instrument]
|
||||
pub(crate) fn write_to_file(&self, path: impl AsRef<Path> + std::fmt::Debug) -> Result<()> {
|
||||
trace!("Persisting cache to disk");
|
||||
create_dir_all(path.as_ref().parent().ok_or(Error::Internal)?)?;
|
||||
serde_json::to_writer(
|
||||
OpenOptions::new()
|
||||
|
@ -1,5 +1,3 @@
|
||||
use slog::{Drain, Logger};
|
||||
use slog_atomic::AtomicSwitch;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
@ -33,28 +31,11 @@ pub(crate) struct Opt {
|
||||
#[structopt(short = "w", long = "workers", default_value = "4")]
|
||||
/// Number of worker threads
|
||||
pub(crate) workers: usize,
|
||||
// #[structopt(
|
||||
// short = "l",
|
||||
// long = "logfile",
|
||||
// parse(from_os_str),
|
||||
// default_value = "./hoc.log"
|
||||
// )]
|
||||
// /// The logfile
|
||||
// pub(crate) logfile: PathBuf,
|
||||
}
|
||||
|
||||
pub(crate) fn init() -> Logger {
|
||||
pub(crate) fn init() {
|
||||
std::env::set_var("RUST_LOG", "actix_web=info,hoc=info");
|
||||
openssl_probe::init_ssl_cert_env_vars();
|
||||
|
||||
let decorator = slog_term::PlainDecorator::new(std::io::stdout());
|
||||
let drain = slog_term::FullFormat::new(decorator).build().fuse();
|
||||
let drain = slog_async::Async::new(drain).build().fuse();
|
||||
let drain = AtomicSwitch::new(drain);
|
||||
|
||||
let root = Logger::root(drain, o!("version" => env!("CARGO_PKG_VERSION")));
|
||||
|
||||
info!(root, "Logging initialized");
|
||||
|
||||
root
|
||||
tracing_subscriber::fmt().init();
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
use crate::error::Result;
|
||||
use std::{fs::read_dir, path::Path, result::Result as StdResult};
|
||||
|
||||
#[instrument]
|
||||
pub(crate) fn count_repositories<P>(repo_path: P) -> Result<usize>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
P: AsRef<Path> + std::fmt::Debug,
|
||||
{
|
||||
trace!("Counting repositories");
|
||||
std::fs::create_dir_all(&repo_path)?;
|
||||
Ok(read_dir(repo_path)?
|
||||
.filter_map(StdResult::ok)
|
||||
|
110
src/main.rs
110
src/main.rs
@ -4,12 +4,10 @@
|
||||
extern crate actix_web;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
// #[macro_use]
|
||||
// extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate tracing;
|
||||
|
||||
mod cache;
|
||||
mod config;
|
||||
@ -37,7 +35,6 @@ use actix_web::{
|
||||
use badge::{Badge, BadgeOptions};
|
||||
use git2::{BranchType, Repository};
|
||||
use number_prefix::NumberPrefix;
|
||||
use slog::Logger;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs::create_dir_all,
|
||||
@ -48,6 +45,7 @@ use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
use tracing::Instrument;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
||||
|
||||
@ -62,7 +60,6 @@ struct GeneratorForm<'a> {
|
||||
pub(crate) struct State {
|
||||
repos: String,
|
||||
cache: String,
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@ -85,13 +82,7 @@ fn pull(path: impl AsRef<Path>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn hoc(
|
||||
repo: &str,
|
||||
repo_dir: &str,
|
||||
cache_dir: &str,
|
||||
branch: &str,
|
||||
logger: &Logger,
|
||||
) -> Result<(u64, String, u64)> {
|
||||
fn hoc(repo: &str, repo_dir: &str, cache_dir: &str, branch: &str) -> Result<(u64, String, u64)> {
|
||||
let repo_dir = format!("{}/{}", repo_dir, repo);
|
||||
let cache_dir = format!("{}/{}.json", cache_dir, repo);
|
||||
let cache_dir = Path::new(&cache_dir);
|
||||
@ -118,16 +109,16 @@ fn hoc(
|
||||
let cache = CacheState::read_from_file(&cache_dir, branch, &head)?;
|
||||
match &cache {
|
||||
CacheState::Current { count, commits, .. } => {
|
||||
info!(logger, "Using cache");
|
||||
info!("Using cache");
|
||||
return Ok((*count, head, *commits));
|
||||
}
|
||||
CacheState::Old { head, .. } => {
|
||||
info!(logger, "Updating cache");
|
||||
info!("Updating cache");
|
||||
arg.push(format!("{}..{}", head, branch));
|
||||
arg_commit_count.push(format!("{}..{}", head, branch));
|
||||
}
|
||||
CacheState::No | CacheState::NoneForBranch(..) => {
|
||||
info!(logger, "Creating cache");
|
||||
info!("Creating cache");
|
||||
arg.push(branch.to_string());
|
||||
arg_commit_count.push(branch.to_string());
|
||||
}
|
||||
@ -190,16 +181,20 @@ where
|
||||
T: Service,
|
||||
{
|
||||
let data = data.into_inner();
|
||||
let logger = state
|
||||
.logger
|
||||
.new(o!("service" => T::domain(), "user" => data.0.clone(), "repo" => data.1.clone()));
|
||||
let span = info_span!(
|
||||
"deleting repository and cache",
|
||||
service = T::domain(),
|
||||
user = data.0.as_str(),
|
||||
repo = data.1.as_str()
|
||||
);
|
||||
let future = async {
|
||||
let repo = format!(
|
||||
"{}/{}/{}",
|
||||
T::domain(),
|
||||
data.0.to_lowercase(),
|
||||
data.1.to_lowercase()
|
||||
);
|
||||
info!(logger, "Deleting cache and repository");
|
||||
info!("Deleting cache and repository");
|
||||
let cache_dir = format!("{}/{}.json", &state.cache, repo);
|
||||
let repo_dir = format!("{}/{}", &state.repos, repo);
|
||||
std::fs::remove_file(&cache_dir).or_else(|e| {
|
||||
@ -223,6 +218,8 @@ where
|
||||
format!("/view/{}/{}/{}", T::url_path(), data.0, data.1),
|
||||
)
|
||||
.finish())
|
||||
};
|
||||
future.instrument(span).await
|
||||
}
|
||||
|
||||
async fn handle_hoc_request<T, F>(
|
||||
@ -236,9 +233,14 @@ where
|
||||
F: Fn(HocResult) -> Result<HttpResponse>,
|
||||
{
|
||||
let data = data.into_inner();
|
||||
let logger = state
|
||||
.logger
|
||||
.new(o!("service" => T::domain(), "user" => data.0.clone(), "repo" => data.1.clone(), "branch" => branch.to_string()));
|
||||
let span = info_span!(
|
||||
"handling hoc calculation",
|
||||
service = T::domain(),
|
||||
user = data.0.as_str(),
|
||||
repo = data.1.as_str(),
|
||||
branch
|
||||
);
|
||||
let future = async {
|
||||
let repo = format!("{}/{}", data.0.to_lowercase(), data.1.to_lowercase());
|
||||
let service_path = format!("{}/{}", T::url_path(), repo);
|
||||
let service_url = format!("{}/{}", T::domain(), repo);
|
||||
@ -248,10 +250,10 @@ where
|
||||
let file = Path::new(&path);
|
||||
if !file.exists() {
|
||||
if !remote_exists {
|
||||
warn!(logger, "Repository does not exist");
|
||||
warn!("Repository does not exist");
|
||||
return mapper(HocResult::NotFound);
|
||||
}
|
||||
info!(logger, "Cloning for the first time");
|
||||
info!("Cloning for the first time");
|
||||
create_dir_all(file)?;
|
||||
let repo = Repository::init_bare(file)?;
|
||||
repo.remote_add_fetch("origin", "refs/heads/*:refs/heads/*")?;
|
||||
@ -259,7 +261,7 @@ where
|
||||
REPO_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
pull(&path)?;
|
||||
let (hoc, head, commits) = hoc(&service_url, &state.repos, &state.cache, branch, &logger)?;
|
||||
let (hoc, head, commits) = hoc(&service_url, &state.repos, &state.cache, branch)?;
|
||||
let hoc_pretty = match NumberPrefix::decimal(hoc as f64) {
|
||||
NumberPrefix::Standalone(hoc) => hoc.to_string(),
|
||||
NumberPrefix::Prefixed(prefix, hoc) => format!("{:.1}{}", hoc, prefix),
|
||||
@ -274,6 +276,8 @@ where
|
||||
service_path,
|
||||
};
|
||||
mapper(res)
|
||||
};
|
||||
future.instrument(span).await
|
||||
}
|
||||
|
||||
pub(crate) async fn json_hoc<T: Service>(
|
||||
@ -296,11 +300,25 @@ pub(crate) async fn json_hoc<T: Service>(
|
||||
handle_hoc_request::<T, _>(state, data, branch, mapper).await
|
||||
}
|
||||
|
||||
fn no_cache_response(body: Vec<u8>) -> HttpResponse {
|
||||
let expiration = SystemTime::now() + Duration::from_secs(30);
|
||||
HttpResponse::Ok()
|
||||
.content_type("image/svg+xml")
|
||||
.set(Expires(expiration.into()))
|
||||
.set(CacheControl(vec![
|
||||
CacheDirective::MaxAge(0u32),
|
||||
CacheDirective::MustRevalidate,
|
||||
CacheDirective::NoCache,
|
||||
CacheDirective::NoStore,
|
||||
]))
|
||||
.body(body)
|
||||
}
|
||||
|
||||
pub(crate) async fn calculate_hoc<T: Service>(
|
||||
state: web::Data<Arc<State>>,
|
||||
data: web::Path<(String, String)>,
|
||||
branch: web::Query<BranchQuery>,
|
||||
) -> Result<HttpResponse> {
|
||||
) -> HttpResponse {
|
||||
let mapper = move |r| match r {
|
||||
HocResult::NotFound => p404(),
|
||||
HocResult::Hoc { hoc_pretty, .. } => {
|
||||
@ -313,21 +331,23 @@ pub(crate) async fn calculate_hoc<T: Service>(
|
||||
// TODO: remove clone
|
||||
let body = badge.to_svg().as_bytes().to_vec();
|
||||
|
||||
let expiration = SystemTime::now() + Duration::from_secs(30);
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("image/svg+xml")
|
||||
.set(Expires(expiration.into()))
|
||||
.set(CacheControl(vec![
|
||||
CacheDirective::MaxAge(0u32),
|
||||
CacheDirective::MustRevalidate,
|
||||
CacheDirective::NoCache,
|
||||
CacheDirective::NoStore,
|
||||
]))
|
||||
.body(body))
|
||||
Ok(no_cache_response(body))
|
||||
}
|
||||
};
|
||||
let branch = branch.branch.as_deref().unwrap_or("master");
|
||||
handle_hoc_request::<T, _>(state, data, branch, mapper).await
|
||||
let error_badge = |_| {
|
||||
let error_badge = Badge::new(BadgeOptions {
|
||||
subject: "Hits-of-Code".to_string(),
|
||||
color: "#ff0000".to_string(),
|
||||
status: "error".to_string(),
|
||||
})
|
||||
.unwrap();
|
||||
let body = error_badge.to_svg().as_bytes().to_vec();
|
||||
no_cache_response(body)
|
||||
};
|
||||
handle_hoc_request::<T, _>(state, data, branch, mapper)
|
||||
.await
|
||||
.unwrap_or_else(error_badge)
|
||||
}
|
||||
|
||||
async fn overview<T: Service>(
|
||||
@ -419,23 +439,19 @@ fn favicon32() -> HttpResponse {
|
||||
HttpResponse::Ok().content_type("image/png").body(FAVICON)
|
||||
}
|
||||
|
||||
async fn start_server(logger: Logger) -> std::io::Result<()> {
|
||||
async fn start_server() -> std::io::Result<()> {
|
||||
let interface = format!("{}:{}", OPT.host, OPT.port);
|
||||
let state = Arc::new(State {
|
||||
repos: OPT.outdir.display().to_string(),
|
||||
cache: OPT.cachedir.display().to_string(),
|
||||
logger,
|
||||
});
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.data(state.clone())
|
||||
.wrap(actix_slog::StructuredLogger::new(
|
||||
state.logger.new(o!("log_type" => "access")),
|
||||
))
|
||||
.wrap(tracing_actix_web::TracingLogger)
|
||||
.wrap(middleware::NormalizePath::new(TrailingSlash::Trim))
|
||||
.service(index)
|
||||
.service(web::resource("/tacit-css.min.css").route(web::get().to(css)))
|
||||
// TODO
|
||||
.service(web::resource("/favicon.ico").route(web::get().to(favicon32)))
|
||||
.service(generate)
|
||||
.service(web::resource("/github/{user}/{repo}").to(calculate_hoc::<GitHub>))
|
||||
@ -469,6 +485,8 @@ async fn start_server(logger: Logger) -> std::io::Result<()> {
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let logger = config::init();
|
||||
start_server(logger).await
|
||||
config::init();
|
||||
let span = info_span!("hoc", version = env!("CARGO_PKG_VERSION"));
|
||||
let _ = span.enter();
|
||||
start_server().instrument(span).await
|
||||
}
|
||||
|
@ -23,11 +23,7 @@ macro_rules! test_service {
|
||||
let cache_dir = dbg!(tempdir().unwrap());
|
||||
let repos = format!("{}/", repo_dir.path().display());
|
||||
let cache = format!("{}/", cache_dir.path().display());
|
||||
let state = dbg!(State {
|
||||
repos,
|
||||
cache,
|
||||
logger: crate::config::init(),
|
||||
});
|
||||
let state = dbg!(State { repos, cache });
|
||||
|
||||
let mut app = test_app!(state, web::resource($path).to($what::<T>));
|
||||
|
||||
|
Reference in New Issue
Block a user