Skip to main content

lean_ctx/core/providers/
config.rs

1#[derive(Clone)]
2pub struct GitLabConfig {
3    pub host: String,
4    pub token: String,
5    pub project_path: Option<String>,
6}
7
8impl std::fmt::Debug for GitLabConfig {
9    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10        f.debug_struct("GitLabConfig")
11            .field("host", &self.host)
12            .field("token", &"[REDACTED]")
13            .field("project_path", &self.project_path)
14            .finish()
15    }
16}
17
18impl GitLabConfig {
19    pub fn from_session() -> Result<Self, String> {
20        super::selected_gitlab::config().map_or_else(Self::from_env, Ok)
21    }
22
23    pub fn from_env() -> Result<Self, String> {
24        let token = super::credentials::var("LEAN_CTX_GITLAB_TOKEN")
25            .or_else(|_| super::credentials::var("GITLAB_TOKEN"))
26            .or_else(|_| super::credentials::var("CI_JOB_TOKEN"))
27            .map_err(|_| {
28                "No GitLab token found. Set GITLAB_TOKEN or LEAN_CTX_GITLAB_TOKEN.".to_string()
29            })?;
30
31        let host = super::credentials::var("GITLAB_HOST")
32            .or_else(|_| super::credentials::var("CI_SERVER_HOST"))
33            .unwrap_or_else(|_| "gitlab.com".to_string());
34
35        let project_path = super::credentials::var("CI_PROJECT_PATH")
36            .ok()
37            .or_else(|| detect_project_from_git_remote(&host));
38
39        Ok(Self {
40            host,
41            token,
42            project_path,
43        })
44    }
45
46    pub fn api_url(&self, endpoint: &str) -> String {
47        // Loopback API fixtures speak plain HTTP; production is HTTPS only.
48        // Also in the unit-test harnesses above the Engine (`test-support`),
49        // never in the `lean-ctx` binary.
50        #[cfg(any(test, feature = "test-support"))]
51        if crate::core::data_dir::sandboxed()
52            && let Some(fixture) = self.host.strip_prefix("http://127.0.0.1:")
53        {
54            return format!("http://127.0.0.1:{fixture}/api/v4{endpoint}");
55        }
56        format!("https://{}/api/v4{}", self.host, endpoint)
57    }
58}
59
60fn detect_project_from_git_remote(host: &str) -> Option<String> {
61    let output = std::process::Command::new("git")
62        .args(["remote", "get-url", "origin"])
63        .output()
64        .ok()?;
65    if !output.status.success() {
66        return None;
67    }
68    let url = String::from_utf8_lossy(&output.stdout).trim().to_string();
69
70    // Handle SSH: git@gitlab.com:group/project.git
71    if let Some(rest) = url.strip_prefix(&format!("git@{host}:")) {
72        return Some(rest.trim_end_matches(".git").to_string());
73    }
74    // Handle HTTPS: https://gitlab.com/group/project.git
75    if let Some(rest) = url.strip_prefix(&format!("https://{host}/")) {
76        return Some(rest.trim_end_matches(".git").to_string());
77    }
78    None
79}