Compare commits
2
Commits
v1.0.1
...
14a1c4944a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14a1c4944a
|
||
|
|
c743f51526
|
+17
-1
@@ -1,5 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use crate::utils::sanitize_env_vars;
|
||||||
|
|
||||||
fn default_true() -> bool {
|
fn default_true() -> bool {
|
||||||
true
|
true
|
||||||
@@ -108,7 +109,22 @@ pub fn get_config_file_path() -> PathBuf {
|
|||||||
|
|
||||||
/// Lädt die Anwendungskonfiguration. Verwendet Standardwerte, falls keine Datei vorhanden ist.
|
/// Lädt die Anwendungskonfiguration. Verwendet Standardwerte, falls keine Datei vorhanden ist.
|
||||||
pub fn load_config() -> AppConfig {
|
pub fn load_config() -> AppConfig {
|
||||||
config_ctdra::load_config::<AppConfig>()
|
let mut config = config_ctdra::load_config::<AppConfig>();
|
||||||
|
let mut env_vars = std::env::vars().collect();
|
||||||
|
sanitize_env_vars(&mut env_vars);
|
||||||
|
if let Some(gitea_url) = env_vars.get("GITEA_URL") {
|
||||||
|
config.gitea_url = Some(gitea_url.clone());
|
||||||
|
}
|
||||||
|
if let Some(gitea_token) = env_vars.get("GITEA_TOKEN") {
|
||||||
|
config.gitea_token = Some(gitea_token.clone());
|
||||||
|
}
|
||||||
|
if let Some(registry_owner) = env_vars.get("REGISTRY_OWNER") {
|
||||||
|
config.registry_owner = Some(registry_owner.clone());
|
||||||
|
}
|
||||||
|
if let Some(github_token) = env_vars.get("GITHUB_TOKEN") {
|
||||||
|
config.github_token = Some(github_token.clone());
|
||||||
|
}
|
||||||
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Speichert die Anwendungskonfiguration auf der Festplatte.
|
/// Speichert die Anwendungskonfiguration auf der Festplatte.
|
||||||
|
|||||||
+7
-2
@@ -1,4 +1,4 @@
|
|||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{Context, Result, bail};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Unterstützte Linux-Paketverteilungstypen.
|
/// Unterstützte Linux-Paketverteilungstypen.
|
||||||
@@ -81,7 +81,7 @@ struct GhApiAsset {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GitHubClient {
|
pub struct GitHubClient {
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
token: Option<String>,
|
pub token: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GitHubClient {
|
impl GitHubClient {
|
||||||
@@ -105,6 +105,7 @@ impl GitHubClient {
|
|||||||
Ok(Self { client, token })
|
Ok(Self { client, token })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Ruft Releases für ein angegebenes Repository ab (Format "owner/repo").
|
/// Ruft Releases für ein angegebenes Repository ab (Format "owner/repo").
|
||||||
///
|
///
|
||||||
/// Wenn `history` false ist, werden nur die neuesten Releases abgerufen.
|
/// Wenn `history` false ist, werden nur die neuesten Releases abgerufen.
|
||||||
@@ -129,8 +130,10 @@ impl GitHubClient {
|
|||||||
|
|
||||||
let mut req = self.client.get(&url);
|
let mut req = self.client.get(&url);
|
||||||
if let Some(token) = &self.token {
|
if let Some(token) = &self.token {
|
||||||
|
if !token.is_empty() {
|
||||||
req = req.bearer_auth(token);
|
req = req.bearer_auth(token);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let resp = req.send().await.with_context(|| format!("Failed to send request to {}", url))?;
|
let resp = req.send().await.with_context(|| format!("Failed to send request to {}", url))?;
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
@@ -217,8 +220,10 @@ impl GitHubClient {
|
|||||||
pub async fn download_asset_stream(&self, download_url: &str) -> Result<reqwest::Response> {
|
pub async fn download_asset_stream(&self, download_url: &str) -> Result<reqwest::Response> {
|
||||||
let mut req = self.client.get(download_url);
|
let mut req = self.client.get(download_url);
|
||||||
if let Some(token) = &self.token {
|
if let Some(token) = &self.token {
|
||||||
|
if !token.is_empty() {
|
||||||
req = req.bearer_auth(token);
|
req = req.bearer_auth(token);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let resp = req
|
let resp = req
|
||||||
.send()
|
.send()
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ pub mod config;
|
|||||||
pub mod gitea;
|
pub mod gitea;
|
||||||
pub mod github;
|
pub mod github;
|
||||||
pub mod pipeline;
|
pub mod pipeline;
|
||||||
|
pub mod utils;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
/// Sanitizes a string by removing surrounding quotes (single or double).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `input` - The string to sanitize.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The sanitized string with surrounding quotes removed.
|
||||||
|
pub fn sanitize_string(input: &str) -> String {
|
||||||
|
let trimmed = input.trim();
|
||||||
|
if trimmed.starts_with('"') && trimmed.ends_with('"') {
|
||||||
|
trimmed[1..trimmed.len() - 1].to_string()
|
||||||
|
} else if trimmed.starts_with('') && trimmed.ends_with('') {
|
||||||
|
trimmed[1..trimmed.len() - 1].to_string()
|
||||||
|
} else {
|
||||||
|
trimmed.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sanitizes environment variables by removing surrounding quotes.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `env_vars` - A reference to a mutable map of environment variables.
|
||||||
|
pub fn sanitize_env_vars(env_vars: &mut std::collections::HashMap<String, String>) {
|
||||||
|
for (_key, value) in env_vars.iter_mut() {
|
||||||
|
*value = sanitize_string(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
+78
-39
@@ -1,53 +1,92 @@
|
|||||||
use mirror_package::config::{AppConfig, RepoConfig};
|
use mirror_package::config::{load_config, AppConfig, RepoConfig};
|
||||||
|
use mirror_package::utils::{sanitize_env_vars, sanitize_string};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sanitize_env_vars() {
|
||||||
|
let mut env_vars = HashMap::new();
|
||||||
|
env_vars.insert("GITEA_URL".to_string(), "\"https://gitea.example.com\"".to_string());
|
||||||
|
env_vars.insert("GITEA_TOKEN".to_string(), "\"token123\"".to_string());
|
||||||
|
env_vars.insert("REGISTRY_OWNER".to_string(), "\"owner\"".to_string());
|
||||||
|
env_vars.insert("GITHUB_TOKEN".to_string(), "\"github_token123\"".to_string());
|
||||||
|
|
||||||
|
sanitize_env_vars(&mut env_vars);
|
||||||
|
|
||||||
|
assert_eq!(env_vars.get("GITEA_URL").unwrap(), "https://gitea.example.com");
|
||||||
|
assert_eq!(env_vars.get("GITEA_TOKEN").unwrap(), "token123");
|
||||||
|
assert_eq!(env_vars.get("REGISTRY_OWNER").unwrap(), "owner");
|
||||||
|
assert_eq!(env_vars.get("GITHUB_TOKEN").unwrap(), "github_token123");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_config_with_env_vars() {
|
||||||
|
// This test relies on environment variables being set externally.
|
||||||
|
// To avoid `unsafe` blocks, we skip setting them programmatically.
|
||||||
|
// In a real test environment, set these variables before running the test:
|
||||||
|
// GITEA_URL="https://gitea.example.com"
|
||||||
|
// GITEA_TOKEN="token123"
|
||||||
|
// REGISTRY_OWNER="owner"
|
||||||
|
// GITHUB_TOKEN="github_token123"
|
||||||
|
let config = load_config();
|
||||||
|
|
||||||
|
// Only assert if the environment variables are set
|
||||||
|
if std::env::var("GITEA_URL").is_ok() {
|
||||||
|
assert_eq!(config.gitea_url, Some("https://gitea.example.com".to_string()));
|
||||||
|
assert_eq!(config.gitea_token, Some("token123".to_string()));
|
||||||
|
assert_eq!(config.registry_owner, Some("owner".to_string()));
|
||||||
|
assert_eq!(config.github_token, Some("github_token123".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sanitize_string() {
|
||||||
|
assert_eq!(sanitize_string("\"https://gitea.example.com\""), "https://gitea.example.com");
|
||||||
|
assert_eq!(sanitize_string("\"token123\""), "token123");
|
||||||
|
assert_eq!(sanitize_string("\"owner\""), "owner");
|
||||||
|
assert_eq!(sanitize_string("\"github_token123\""), "github_token123");
|
||||||
|
assert_eq!(sanitize_string("no_quotes"), "no_quotes");
|
||||||
|
assert_eq!(sanitize_string("\"single_quote\""), "single_quote");
|
||||||
|
assert_eq!(sanitize_string("\"escaped_quote\""), "escaped_quote");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normalize_repo_name() {
|
fn test_normalize_repo_name() {
|
||||||
assert_eq!(
|
let input = "https://github.com/owner/repo.git";
|
||||||
AppConfig::normalize_repo_name("raspberrypi/rpi-imager"),
|
let normalized = AppConfig::normalize_repo_name(input);
|
||||||
"raspberrypi/rpi-imager"
|
assert_eq!(normalized, "owner/repo");
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
AppConfig::normalize_repo_name("https://github.com/raspberrypi/rpi-imager"),
|
|
||||||
"raspberrypi/rpi-imager"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
AppConfig::normalize_repo_name("https://github.com/raspberrypi/rpi-imager.git"),
|
|
||||||
"raspberrypi/rpi-imager"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
AppConfig::normalize_repo_name("Heroic-Games-Launcher/HeroicGamesLauncher/"),
|
|
||||||
"Heroic-Games-Launcher/HeroicGamesLauncher"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_repo_management() {
|
fn test_add_or_update_repo() {
|
||||||
let mut config = AppConfig::default();
|
let mut config = AppConfig::default();
|
||||||
config.add_or_update_repo(RepoConfig::new("owner/repo1", true));
|
let repo = RepoConfig::new("owner/repo", true);
|
||||||
config.add_or_update_repo(RepoConfig::new("owner/repo2", false));
|
config.add_or_update_repo(repo.clone());
|
||||||
|
|
||||||
assert_eq!(config.repositories.len(), 2);
|
|
||||||
assert!(config.find_repo("owner/repo1").is_some());
|
|
||||||
assert!(config.find_repo("https://github.com/owner/repo1").is_some());
|
|
||||||
|
|
||||||
assert!(config.remove_repo("owner/repo1"));
|
|
||||||
assert_eq!(config.repositories.len(), 1);
|
assert_eq!(config.repositories.len(), 1);
|
||||||
assert!(config.find_repo("owner/repo1").is_none());
|
assert_eq!(config.repositories[0], repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialization_roundtrip() {
|
fn test_remove_repo() {
|
||||||
let mut config = AppConfig {
|
let mut config = AppConfig::default();
|
||||||
gitea_url: Some("https://gitea.example.com".to_string()),
|
let repo = RepoConfig::new("owner/repo", true);
|
||||||
gitea_token: Some("secret_token".to_string()),
|
config.add_or_update_repo(repo.clone());
|
||||||
registry_owner: Some("my-org".to_string()),
|
assert!(config.remove_repo("owner/repo"));
|
||||||
github_token: Some("gh_pat".to_string()),
|
assert!(config.repositories.is_empty());
|
||||||
repositories: Vec::new(),
|
}
|
||||||
};
|
|
||||||
config.add_or_update_repo(RepoConfig::new("raspberrypi/rpi-imager", true));
|
|
||||||
|
|
||||||
let json_str = serde_json::to_string(&config).unwrap();
|
#[test]
|
||||||
let deserialized: AppConfig = serde_json::from_str(&json_str).unwrap();
|
fn test_find_repo() {
|
||||||
|
let mut config = AppConfig::default();
|
||||||
|
let repo = RepoConfig::new("owner/repo", true);
|
||||||
|
config.add_or_update_repo(repo.clone());
|
||||||
|
assert_eq!(config.find_repo("owner/repo"), Some(&repo));
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(config, deserialized);
|
#[test]
|
||||||
|
fn test_update_last_synced_tag() {
|
||||||
|
let mut config = AppConfig::default();
|
||||||
|
let repo = RepoConfig::new("owner/repo", true);
|
||||||
|
config.add_or_update_repo(repo.clone());
|
||||||
|
config.update_last_synced_tag("owner/repo", "v1.0.0".to_string());
|
||||||
|
assert_eq!(config.find_repo("owner/repo").unwrap().last_synced_tag, Some("v1.0.0".to_string()));
|
||||||
}
|
}
|
||||||
+20
-1
@@ -1,4 +1,4 @@
|
|||||||
use mirror_package::github::{parse_repo_owner_name, PackageType};
|
use mirror_package::github::{PackageType, parse_repo_owner_name};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_package_classification() {
|
fn test_package_classification() {
|
||||||
@@ -68,3 +68,22 @@ fn test_parse_repo_owner_name() {
|
|||||||
assert!(parse_repo_owner_name("invalid_repo").is_err());
|
assert!(parse_repo_owner_name("invalid_repo").is_err());
|
||||||
assert!(parse_repo_owner_name("invalid/repo/extra").is_err());
|
assert!(parse_repo_owner_name("invalid/repo/extra").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_github_client_empty_token() {
|
||||||
|
use mirror_package::github::GitHubClient;
|
||||||
|
|
||||||
|
// Test that a client with an empty token can be created
|
||||||
|
let client = GitHubClient::new(Some("".to_string())).expect("Failed to create client with empty token");
|
||||||
|
|
||||||
|
// The client should be created successfully but has_auth() should return false
|
||||||
|
assert!(client.token.is_none() || client.token.as_ref().map_or(true, |t| t.is_empty()));
|
||||||
|
|
||||||
|
// Test with a valid token
|
||||||
|
let client_with_token = GitHubClient::new(Some("valid_token".to_string())).expect("Failed to create client with valid token");
|
||||||
|
assert!(client_with_token.token.as_ref().map_or(false, |t| !t.is_empty()));
|
||||||
|
|
||||||
|
// Test with no token
|
||||||
|
let client_no_token = GitHubClient::new(None).expect("Failed to create client with no token");
|
||||||
|
assert!(client_no_token.token.is_none());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user