feat: per-repo issue tracker, Gitea support, PR review pipeline #10

Merged
sharang merged 7 commits from feat/per-repo-tracker-config into main 2026-03-11 12:14:00 +00:00
3 changed files with 29 additions and 31 deletions
Showing only changes of commit f11e6d44cc - Show all commits

View File

@@ -914,27 +914,15 @@ impl PipelineOrchestrator {
/// e.g. "https://gitea.example.com/owner/repo.git" → "https://gitea.example.com" /// e.g. "https://gitea.example.com/owner/repo.git" → "https://gitea.example.com"
/// e.g. "ssh://git@gitea.example.com:22/owner/repo.git" → "https://gitea.example.com" /// e.g. "ssh://git@gitea.example.com:22/owner/repo.git" → "https://gitea.example.com"
fn extract_base_url(git_url: &str) -> Option<String> { fn extract_base_url(git_url: &str) -> Option<String> {
if git_url.starts_with("http://") || git_url.starts_with("https://") { if let Some(rest) = git_url.strip_prefix("https://") {
// https://host/path... → take scheme + host let host = rest.split('/').next()?;
let without_scheme = if git_url.starts_with("https://") { Some(format!("https://{host}"))
&git_url[8..] } else if let Some(rest) = git_url.strip_prefix("http://") {
} else { let host = rest.split('/').next()?;
&git_url[7..] Some(format!("http://{host}"))
}; } else if let Some(rest) = git_url.strip_prefix("ssh://") {
let host = without_scheme.split('/').next()?;
let scheme = if git_url.starts_with("https://") {
"https"
} else {
"http"
};
Some(format!("{scheme}://{host}"))
} else if git_url.starts_with("ssh://") {
// ssh://git@host:port/path → extract host // ssh://git@host:port/path → extract host
let after_scheme = &git_url[6..]; let after_at = rest.find('@').map(|i| &rest[i + 1..]).unwrap_or(rest);
let after_at = after_scheme
.find('@')
.map(|i| &after_scheme[i + 1..])
.unwrap_or(after_scheme);
let host = after_at.split(&[':', '/'][..]).next()?; let host = after_at.split(&[':', '/'][..]).next()?;
Some(format!("https://{host}")) Some(format!("https://{host}"))
} else if let Some(at_pos) = git_url.find('@') { } else if let Some(at_pos) = git_url.find('@') {

View File

@@ -30,6 +30,7 @@ pub async fn fetch_repositories(page: u64) -> Result<RepositoryListResponse, Ser
} }
#[server] #[server]
#[allow(clippy::too_many_arguments)]
pub async fn add_repository( pub async fn add_repository(
name: String, name: String,
git_url: String, git_url: String,

View File

@@ -677,23 +677,32 @@ fn license_type_class(is_copyleft: bool) -> &'static str {
#[cfg(feature = "web")] #[cfg(feature = "web")]
fn trigger_download(content: &str, filename: &str) { fn trigger_download(content: &str, filename: &str) {
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
let window = web_sys::window().expect("no window"); let Some(window) = web_sys::window() else {
let document = window.document().expect("no document"); return;
};
let Some(document) = window.document() else {
return;
};
let blob_parts = js_sys::Array::new(); let blob_parts = js_sys::Array::new();
blob_parts.push(&wasm_bindgen::JsValue::from_str(content)); blob_parts.push(&wasm_bindgen::JsValue::from_str(content));
let mut opts = web_sys::BlobPropertyBag::new(); let opts = web_sys::BlobPropertyBag::new();
opts.type_("application/json"); opts.set_type("application/json");
let blob = web_sys::Blob::new_with_str_sequence_and_options(&blob_parts, &opts).expect("blob"); let Ok(blob) = web_sys::Blob::new_with_str_sequence_and_options(&blob_parts, &opts) else {
return;
};
let url = web_sys::Url::create_object_url_with_blob(&blob).expect("object url"); let Ok(url) = web_sys::Url::create_object_url_with_blob(&blob) else {
return;
};
let a: web_sys::HtmlAnchorElement = document let Ok(el) = document.create_element("a") else {
.create_element("a") return;
.expect("create a") };
.dyn_into() let Ok(a) = el.dyn_into::<web_sys::HtmlAnchorElement>() else {
.expect("cast"); return;
};
a.set_href(&url); a.set_href(&url);
a.set_download(filename); a.set_download(filename);
a.click(); a.click();