Compare commits
3 Commits
5c44ee91fb
...
v0.9.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60075cd400 | ||
|
|
ddab186315 | ||
|
|
d6d1686f8e |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1324,7 +1324,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lore"
|
name = "lore"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"asupersync",
|
"asupersync",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lore"
|
name = "lore"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Gitlore - Local GitLab data management with semantic search"
|
description = "Gitlore - Local GitLab data management with semantic search"
|
||||||
authors = ["Taylor Eernisse"]
|
authors = ["Taylor Eernisse"]
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ fn print_combined_ingest_json(
|
|||||||
notes_upserted: mrs.notes_upserted,
|
notes_upserted: mrs.notes_upserted,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
@@ -975,9 +975,7 @@ async fn handle_auth_test(
|
|||||||
name: result.name.clone(),
|
name: result.name.clone(),
|
||||||
gitlab_url: result.base_url.clone(),
|
gitlab_url: result.base_url.clone(),
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else {
|
} else {
|
||||||
@@ -1038,9 +1036,7 @@ async fn handle_doctor(
|
|||||||
success: result.success,
|
success: result.success,
|
||||||
checks: result.checks,
|
checks: result.checks,
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else {
|
} else {
|
||||||
@@ -1085,9 +1081,7 @@ fn handle_version(robot_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Some(git_hash)
|
Some(git_hash)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else if git_hash.is_empty() {
|
} else if git_hash.is_empty() {
|
||||||
@@ -1245,9 +1239,7 @@ async fn handle_migrate(
|
|||||||
after_version,
|
after_version,
|
||||||
migrated: after_version > before_version,
|
migrated: after_version > before_version,
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else if after_version > before_version {
|
} else if after_version > before_version {
|
||||||
@@ -1962,9 +1954,7 @@ async fn handle_health(
|
|||||||
schema_version,
|
schema_version,
|
||||||
actions,
|
actions,
|
||||||
},
|
},
|
||||||
meta: RobotMeta {
|
meta: RobotMeta::new(start.elapsed().as_millis() as u64),
|
||||||
elapsed_ms: start.elapsed().as_millis() as u64,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
println!("{}", serde_json::to_string(&output)?);
|
println!("{}", serde_json::to_string(&output)?);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
|
|||||||
"mentioned_in": "[{entity_type:string, project:string, iid:int, title:string, state:string, attention_state:string, attention_reason:string, updated_at_iso:string, web_url:string?}]",
|
"mentioned_in": "[{entity_type:string, project:string, iid:int, title:string, state:string, attention_state:string, attention_reason:string, updated_at_iso:string, web_url:string?}]",
|
||||||
"activity": "[{timestamp_iso:string, event_type:string, entity_type:string, entity_iid:int, project:string, actor:string?, is_own:bool, summary:string, body_preview:string?}]"
|
"activity": "[{timestamp_iso:string, event_type:string, entity_type:string, entity_iid:int, project:string, actor:string?, is_own:bool, summary:string, body_preview:string?}]"
|
||||||
},
|
},
|
||||||
"meta": {"elapsed_ms": "int"}
|
"meta": {"elapsed_ms": "int", "gitlab_base_url": "string (GitLab instance URL for constructing entity links: {base_url}/{project}/-/issues/{iid})"}
|
||||||
},
|
},
|
||||||
"fields_presets": {
|
"fields_presets": {
|
||||||
"me_items_minimal": ["iid", "title", "attention_state", "attention_reason", "updated_at_iso"],
|
"me_items_minimal": ["iid", "title", "attention_state", "attention_reason", "updated_at_iso"],
|
||||||
@@ -396,7 +396,8 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
|
|||||||
"since_default": "1d for activity feed",
|
"since_default": "1d for activity feed",
|
||||||
"issue_filter": "Only In Progress / In Review status issues shown",
|
"issue_filter": "Only In Progress / In Review status issues shown",
|
||||||
"since_last_check": "Cursor-based inbox showing events since last run. Null on first run (no cursor yet). Groups events by entity (issue/MR). Sources: others' comments on your items, @mentions, assignment/review-request notes. Cursor auto-advances after each run. Use --reset-cursor to clear.",
|
"since_last_check": "Cursor-based inbox showing events since last run. Null on first run (no cursor yet). Groups events by entity (issue/MR). Sources: others' comments on your items, @mentions, assignment/review-request notes. Cursor auto-advances after each run. Use --reset-cursor to clear.",
|
||||||
"cursor_persistence": "Stored per user in ~/.local/share/lore/me_cursor_<username>.json. --project filters display only for since-last-check; cursor still advances for all projects for that user."
|
"cursor_persistence": "Stored per user in ~/.local/share/lore/me_cursor_<username>.json. --project filters display only for since-last-check; cursor still advances for all projects for that user.",
|
||||||
|
"url_construction": "Use meta.gitlab_base_url + project + entity_type + iid to build links: {gitlab_base_url}/{project}/-/{issues|merge_requests}/{iid}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"robot-docs": {
|
"robot-docs": {
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ pub fn print_event_count_json(counts: &EventCounts, elapsed_ms: u64) {
|
|||||||
},
|
},
|
||||||
total: counts.total(),
|
total: counts.total(),
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
@@ -325,7 +325,7 @@ pub fn print_count_json(result: &CountResult, elapsed_ms: u64) {
|
|||||||
system_excluded: result.system_count,
|
system_excluded: result.system_count,
|
||||||
breakdown,
|
breakdown,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub fn print_cron_install_json(result: &CronInstallResult, elapsed_ms: u64) {
|
|||||||
log_path: result.log_path.display().to_string(),
|
log_path: result.log_path.display().to_string(),
|
||||||
replaced: result.replaced,
|
replaced: result.replaced,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
if let Ok(json) = serde_json::to_string(&output) {
|
if let Ok(json) = serde_json::to_string(&output) {
|
||||||
println!("{json}");
|
println!("{json}");
|
||||||
@@ -128,7 +128,7 @@ pub fn print_cron_uninstall_json(result: &CronUninstallResult, elapsed_ms: u64)
|
|||||||
action: "uninstall",
|
action: "uninstall",
|
||||||
was_installed: result.was_installed,
|
was_installed: result.was_installed,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
if let Ok(json) = serde_json::to_string(&output) {
|
if let Ok(json) = serde_json::to_string(&output) {
|
||||||
println!("{json}");
|
println!("{json}");
|
||||||
@@ -284,7 +284,7 @@ pub fn print_cron_status_json(info: &CronStatusInfo, elapsed_ms: u64) {
|
|||||||
last_sync_at: info.last_sync.as_ref().map(|s| s.started_at_iso.clone()),
|
last_sync_at: info.last_sync.as_ref().map(|s| s.started_at_iso.clone()),
|
||||||
last_sync_status: info.last_sync.as_ref().map(|s| s.status.clone()),
|
last_sync_status: info.last_sync.as_ref().map(|s| s.status.clone()),
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
if let Ok(json) = serde_json::to_string(&output) {
|
if let Ok(json) = serde_json::to_string(&output) {
|
||||||
println!("{json}");
|
println!("{json}");
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ pub fn print_drift_human(response: &DriftResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_drift_json(response: &DriftResponse, elapsed_ms: u64) {
|
pub fn print_drift_json(response: &DriftResponse, elapsed_ms: u64) {
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": response,
|
"data": response,
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ pub fn print_embed_json(result: &EmbedCommandResult, elapsed_ms: u64) {
|
|||||||
let output = EmbedJsonOutput {
|
let output = EmbedJsonOutput {
|
||||||
ok: true,
|
ok: true,
|
||||||
data: result,
|
data: result,
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
Ok(json) => println!("{json}"),
|
Ok(json) => println!("{json}"),
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ pub fn print_generate_docs_json(result: &GenerateDocsResult, elapsed_ms: u64) {
|
|||||||
unchanged: result.unchanged,
|
unchanged: result.unchanged,
|
||||||
errored: result.errored,
|
errored: result.errored,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
Ok(json) => println!("{json}"),
|
Ok(json) => println!("{json}"),
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ pub fn print_ingest_summary_json(result: &IngestResult, elapsed_ms: u64) {
|
|||||||
status_enrichment,
|
status_enrichment,
|
||||||
status_enrichment_errors: result.status_enrichment_errors,
|
status_enrichment_errors: result.status_enrichment_errors,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ pub fn print_list_mrs(result: &MrListResult) {
|
|||||||
|
|
||||||
pub fn print_list_mrs_json(result: &MrListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
pub fn print_list_mrs_json(result: &MrListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
||||||
let json_result = MrListResultJson::from(result);
|
let json_result = MrListResultJson::from(result);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ pub fn print_list_notes(result: &NoteListResult) {
|
|||||||
|
|
||||||
pub fn print_list_notes_json(result: &NoteListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
pub fn print_list_notes_json(result: &NoteListResult, elapsed_ms: u64, fields: Option<&[String]>) {
|
||||||
let json_result = NoteListResultJson::from(result);
|
let json_result = NoteListResultJson::from(result);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ pub fn run_me(config: &Config, args: &MeArgs, robot_mode: bool) -> Result<()> {
|
|||||||
|
|
||||||
if robot_mode {
|
if robot_mode {
|
||||||
let fields = args.fields.as_deref();
|
let fields = args.fields.as_deref();
|
||||||
render_robot::print_me_json(&dashboard, elapsed_ms, fields)?;
|
render_robot::print_me_json(&dashboard, elapsed_ms, fields, &config.gitlab.base_url)?;
|
||||||
} else if show_all {
|
} else if show_all {
|
||||||
render_human::print_me_dashboard(&dashboard, single_project);
|
render_human::print_me_dashboard(&dashboard, single_project);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ pub fn print_me_json(
|
|||||||
dashboard: &MeDashboard,
|
dashboard: &MeDashboard,
|
||||||
elapsed_ms: u64,
|
elapsed_ms: u64,
|
||||||
fields: Option<&[String]>,
|
fields: Option<&[String]>,
|
||||||
|
gitlab_base_url: &str,
|
||||||
) -> crate::core::error::Result<()> {
|
) -> crate::core::error::Result<()> {
|
||||||
let envelope = MeJsonEnvelope {
|
let envelope = MeJsonEnvelope {
|
||||||
ok: true,
|
ok: true,
|
||||||
data: MeDataJson::from_dashboard(dashboard),
|
data: MeDataJson::from_dashboard(dashboard),
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::with_base_url(elapsed_ms, gitlab_base_url),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut value = serde_json::to_value(&envelope)
|
let mut value = serde_json::to_value(&envelope)
|
||||||
|
|||||||
@@ -558,7 +558,7 @@ pub fn print_related_human(response: &RelatedResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_related_json(response: &RelatedResponse, elapsed_ms: u64) {
|
pub fn print_related_json(response: &RelatedResponse, elapsed_ms: u64) {
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": response,
|
"data": response,
|
||||||
|
|||||||
@@ -557,7 +557,7 @@ impl From<&MrNoteDetail> for MrNoteDetailJson {
|
|||||||
|
|
||||||
pub fn print_show_issue_json(issue: &IssueDetail, elapsed_ms: u64) {
|
pub fn print_show_issue_json(issue: &IssueDetail, elapsed_ms: u64) {
|
||||||
let json_result = IssueDetailJson::from(issue);
|
let json_result = IssueDetailJson::from(issue);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
@@ -571,7 +571,7 @@ pub fn print_show_issue_json(issue: &IssueDetail, elapsed_ms: u64) {
|
|||||||
|
|
||||||
pub fn print_show_mr_json(mr: &MrDetail, elapsed_ms: u64) {
|
pub fn print_show_mr_json(mr: &MrDetail, elapsed_ms: u64) {
|
||||||
let json_result = MrDetailJson::from(mr);
|
let json_result = MrDetailJson::from(mr);
|
||||||
let meta = RobotMeta { elapsed_ms };
|
let meta = RobotMeta::new(elapsed_ms);
|
||||||
let output = serde_json::json!({
|
let output = serde_json::json!({
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"data": json_result,
|
"data": json_result,
|
||||||
|
|||||||
@@ -583,7 +583,7 @@ pub fn print_stats_json(result: &StatsResult, elapsed_ms: u64) {
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
Ok(json) => println!("{json}"),
|
Ok(json) => println!("{json}"),
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ pub fn print_sync_status_json(result: &SyncStatusResult, elapsed_ms: u64) {
|
|||||||
system_notes: result.summary.system_note_count,
|
system_notes: result.summary.system_note_count,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
match serde_json::to_string(&output) {
|
match serde_json::to_string(&output) {
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ pub fn print_who_json(run: &WhoRun, args: &WhoArgs, elapsed_ms: u64) {
|
|||||||
resolved_input,
|
resolved_input,
|
||||||
result: data,
|
result: data,
|
||||||
},
|
},
|
||||||
meta: RobotMeta { elapsed_ms },
|
meta: RobotMeta::new(elapsed_ms),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut value = serde_json::to_value(&output).unwrap_or_else(|e| {
|
let mut value = serde_json::to_value(&output).unwrap_or_else(|e| {
|
||||||
|
|||||||
@@ -3,6 +3,26 @@ use serde::Serialize;
|
|||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct RobotMeta {
|
pub struct RobotMeta {
|
||||||
pub elapsed_ms: u64,
|
pub elapsed_ms: u64,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub gitlab_base_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RobotMeta {
|
||||||
|
/// Standard meta with timing only.
|
||||||
|
pub fn new(elapsed_ms: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
elapsed_ms,
|
||||||
|
gitlab_base_url: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Meta with GitLab base URL for URL construction by consumers.
|
||||||
|
pub fn with_base_url(elapsed_ms: u64, base_url: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
elapsed_ms,
|
||||||
|
gitlab_base_url: Some(base_url.trim_end_matches('/').to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter JSON object fields in-place for `--fields` support.
|
/// Filter JSON object fields in-place for `--fields` support.
|
||||||
@@ -133,4 +153,27 @@ mod tests {
|
|||||||
let expanded = expand_fields_preset(&fields, "notes");
|
let expanded = expand_fields_preset(&fields, "notes");
|
||||||
assert_eq!(expanded, ["id", "body"]);
|
assert_eq!(expanded, ["id", "body"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn meta_new_omits_base_url() {
|
||||||
|
let meta = RobotMeta::new(42);
|
||||||
|
let json = serde_json::to_value(&meta).unwrap();
|
||||||
|
assert_eq!(json["elapsed_ms"], 42);
|
||||||
|
assert!(json.get("gitlab_base_url").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn meta_with_base_url_includes_it() {
|
||||||
|
let meta = RobotMeta::with_base_url(99, "https://gitlab.example.com");
|
||||||
|
let json = serde_json::to_value(&meta).unwrap();
|
||||||
|
assert_eq!(json["elapsed_ms"], 99);
|
||||||
|
assert_eq!(json["gitlab_base_url"], "https://gitlab.example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn meta_with_base_url_strips_trailing_slash() {
|
||||||
|
let meta = RobotMeta::with_base_url(0, "https://gitlab.example.com/");
|
||||||
|
let json = serde_json::to_value(&meta).unwrap();
|
||||||
|
assert_eq!(json["gitlab_base_url"], "https://gitlab.example.com");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user