Skip to content

Commit

Permalink
fix: cache github release information for 24 hours
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Nov 30, 2024
1 parent f42f010 commit d18ad1d
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/backend/aqua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Backend for AquaBackend {
Ok(vec!["cosign", "slsa-verifier"])
}

fn _list_remote_versions(&self) -> eyre::Result<Vec<String>> {
fn _list_remote_versions(&self) -> Result<Vec<String>> {
let pkg = AQUA_REGISTRY.package(&self.id)?;
if !pkg.repo_owner.is_empty() && !pkg.repo_name.is_empty() {
let versions = if let Some("github_tag") = pkg.version_source.as_deref() {
Expand Down
2 changes: 1 addition & 1 deletion src/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ pub use std::time::Duration;

pub const HOURLY: Duration = Duration::from_secs(60 * 60);
pub const DAILY: Duration = Duration::from_secs(60 * 60 * 24);
//pub const WEEKLY: Duration = Duration::from_secs(60 * 60 * 24 * 7);
// pub const WEEKLY: Duration = Duration::from_secs(60 * 60 * 24 * 7);
103 changes: 90 additions & 13 deletions src/github.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
use crate::env;
use crate::cache::{CacheManager, CacheManagerBuilder};
use crate::{dirs, duration, env};
use eyre::Result;
use heck::ToKebabCase;
use once_cell::sync::Lazy;
use reqwest::header::HeaderMap;
use serde_derive::Deserialize;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::RwLock;
use std::sync::RwLockReadGuard;
use xx::regex;

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GithubRelease {
pub tag_name: String,
// pub name: Option<String>,
Expand All @@ -14,20 +22,74 @@ pub struct GithubRelease {
pub assets: Vec<GithubAsset>,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GithubTag {
pub name: String,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GithubAsset {
pub name: String,
// pub size: u64,
pub browser_download_url: String,
}

pub fn list_releases(repo: &str) -> eyre::Result<Vec<GithubRelease>> {
let url = format!("https://api.github.com/repos/{}/releases", repo);
type CacheGroup<T> = HashMap<String, CacheManager<T>>;

static RELEASES_CACHE: Lazy<RwLock<CacheGroup<Vec<GithubRelease>>>> = Lazy::new(Default::default);

static RELEASE_CACHE: Lazy<RwLock<CacheGroup<GithubRelease>>> = Lazy::new(Default::default);

static TAGS_CACHE: Lazy<RwLock<CacheGroup<Vec<String>>>> = Lazy::new(Default::default);

fn get_tags_cache(key: &str) -> RwLockReadGuard<'_, CacheGroup<Vec<String>>> {
TAGS_CACHE
.write()
.unwrap()
.entry(key.to_string())
.or_insert_with(|| {
CacheManagerBuilder::new(cache_dir().join(format!("{key}-tags.msgpack.z")))
.with_fresh_duration(Some(duration::DAILY))
.build()
});
TAGS_CACHE.read().unwrap()
}

fn get_releases_cache(key: &str) -> RwLockReadGuard<'_, CacheGroup<Vec<GithubRelease>>> {
RELEASES_CACHE
.write()
.unwrap()
.entry(key.to_string())
.or_insert_with(|| {
CacheManagerBuilder::new(cache_dir().join(format!("{key}-releases.msgpack.z")))
.with_fresh_duration(Some(duration::DAILY))
.build()
});
RELEASES_CACHE.read().unwrap()
}

fn get_release_cache<'a>(key: &str) -> RwLockReadGuard<'a, CacheGroup<GithubRelease>> {
RELEASE_CACHE
.write()
.unwrap()
.entry(key.to_string())
.or_insert_with(|| {
CacheManagerBuilder::new(cache_dir().join(format!("{key}.msgpack.z")))
.with_fresh_duration(Some(duration::DAILY))
.build()
});
RELEASE_CACHE.read().unwrap()
}

pub fn list_releases(repo: &str) -> Result<Vec<GithubRelease>> {
let key = repo.to_kebab_case();
let cache = get_releases_cache(&key);
let cache = cache.get(&key).unwrap();
Ok(cache.get_or_try_init(|| list_releases_(repo))?.to_vec())
}

fn list_releases_(repo: &str) -> Result<Vec<GithubRelease>> {
let url = format!("https://api.github.com/repos/{repo}/releases");
let (mut releases, mut headers) =
crate::http::HTTP_FETCH.json_headers::<Vec<GithubRelease>, _>(url)?;

Expand All @@ -42,7 +104,14 @@ pub fn list_releases(repo: &str) -> eyre::Result<Vec<GithubRelease>> {
Ok(releases)
}

pub fn list_tags(repo: &str) -> eyre::Result<Vec<String>> {
pub fn list_tags(repo: &str) -> Result<Vec<String>> {
let key = repo.to_kebab_case();
let cache = get_tags_cache(&key);
let cache = cache.get(&key).unwrap();
Ok(cache.get_or_try_init(|| list_tags_(repo))?.to_vec())
}

fn list_tags_(repo: &str) -> Result<Vec<String>> {
let url = format!("https://api.github.com/repos/{}/tags", repo);
let (mut tags, mut headers) = crate::http::HTTP_FETCH.json_headers::<Vec<GithubTag>, _>(url)?;

Expand All @@ -57,11 +126,15 @@ pub fn list_tags(repo: &str) -> eyre::Result<Vec<String>> {
Ok(tags.into_iter().map(|t| t.name).collect())
}

pub fn get_release(repo: &str, tag: &str) -> eyre::Result<GithubRelease> {
let url = format!(
"https://api.github.com/repos/{}/releases/tags/{}",
repo, tag
);
pub fn get_release(repo: &str, tag: &str) -> Result<GithubRelease> {
let key = format!("{repo}-{tag}").to_kebab_case();
let cache = get_release_cache(&key);
let cache = cache.get(&key).unwrap();
Ok(cache.get_or_try_init(|| get_release_(repo, tag))?.clone())
}

fn get_release_(repo: &str, tag: &str) -> Result<GithubRelease> {
let url = format!("https://api.github.com/repos/{repo}/releases/tags/{tag}");
crate::http::HTTP_FETCH.json(url)
}

Expand All @@ -74,3 +147,7 @@ fn next_page(headers: &HeaderMap) -> Option<String> {
.captures(&link)
.map(|c| c.get(1).unwrap().as_str().to_string())
}

fn cache_dir() -> PathBuf {
dirs::CACHE.join("github")
}

0 comments on commit d18ad1d

Please sign in to comment.