fix(#1868): use HIVE_FORGE_URL for internal forge calls

Replace the hardcoded FORGE_HTTP const with forge_http_base() which
reads HIVE_FORGE_URL from the environment (already set unconditionally
by hive-c0re.nix to http://<forge.domain>). Add forge_git_url() helper
that inserts core:<token> credentials between scheme and authority for
git push/clone URLs.

All call sites updated:
- forge/mod.rs: api() OnceLock + new forge_git_url/forge_http_base fns
- forge/repos.rs: push_meta, push_config, ensure_meta_remote
- forge/pr_merge.rs: tokenised_repo_url delegate + test loosened
- workers/knowledge.rs: clone + push URLs
- socket_server/mod.rs: clone_url in RepoCreated response

No new env var: HIVE_FORGE_URL was already the right knob (mara).

Closes #1868. Closes #2174 (this supersedes the operators-team fix from
the closed #2218, which is re-applied in the ensure_operators_team call
that was already merged separately).
This commit is contained in:
atlas 2026-07-08 22:34:17 +02:00 committed by mara
commit 031edbd41f
6 changed files with 57 additions and 23 deletions

View file

@ -31,7 +31,34 @@ use users::{
};
const FORGE_CONTAINER: &str = "hive-forge";
pub(crate) const FORGE_HTTP: &str = "http://localhost:3000";
/// Base HTTP URL for the local Forgejo instance. Reads `HIVE_FORGE_URL`
/// from the environment (set unconditionally by `hive-c0re.nix` to
/// `http://<forge.domain>`) so the forge port is never hardcoded.
/// Falls back to `http://localhost:3000` for bare runs outside the
/// NixOS module (tests, manual invocation).
pub(crate) fn forge_http_base() -> &'static str {
static BASE: OnceLock<String> = OnceLock::new();
BASE.get_or_init(|| {
std::env::var("HIVE_FORGE_URL").unwrap_or_else(|_| "http://localhost:3000".to_string())
})
}
/// Token-in-URL git remote for `repo` (e.g. `"core/meta"`). Inserts
/// `core:<token>` credentials between the scheme and authority of
/// [`forge_http_base()`] — the form git accepts for inline auth.
pub(crate) fn forge_git_url(token: &str, repo: &str) -> String {
let base = forge_http_base();
// Split on "://" to isolate scheme + authority. The base URL always
// contains "://" (validated fallback + `HIVE_FORGE_URL` is
// operator-set and expected to be well-formed).
if let Some((scheme, host)) = base.split_once("://") {
format!("{scheme}://core:{token}@{host}/{repo}.git")
} else {
format!("http://core:{token}@localhost:3000/{repo}.git")
}
}
/// Forgejo org grouping every agent's config repo. Core is a site admin
/// and reads + writes every repo here. As of the agent-config-PR flow each
/// agent is a **write collaborator on its own** `agent-configs/<name>` repo —
@ -50,7 +77,7 @@ const CONFIG_ORG: &str = "agent-configs";
/// (i.e. `core` user) can push.
const SHARED_ORG: &str = "internal";
/// The shared docs repo inside `SHARED_ORG`. Cloneable by every agent
/// at `{FORGE_HTTP}/internal/docs.git`.
/// at `{forge_http_base()}/internal/docs.git`.
const SHARED_DOCS_REPO: &str = "docs";
/// The hive-wide knowledge repo inside `SHARED_ORG`. Public — agents
/// can fork it and open PRs without explicit collaborator grants.
@ -108,7 +135,7 @@ async fn forge_admin(args: &[&str]) -> Result<String> {
Ok(stdout)
}
/// Typed Forgejo API client for the local forge ([`FORGE_HTTP`]),
/// Typed Forgejo API client for the local forge ([`forge_http_base()`]),
/// authenticated as `token`. All Forgejo API calls that don't shell
/// out to `forgejo admin` go through clients built here — one place
/// for the base URL and auth. Tokens differ per call site (core admin
@ -119,7 +146,7 @@ async fn forge_admin(args: &[&str]) -> Result<String> {
pub(crate) fn api(token: &str) -> Result<Forgejo> {
static URL: OnceLock<Url> = OnceLock::new();
let url = URL
.get_or_init(|| Url::parse(FORGE_HTTP).expect("FORGE_HTTP is a valid URL"))
.get_or_init(|| Url::parse(forge_http_base()).expect("forge_http_base() is a valid URL"))
.clone();
Forgejo::new(Auth::Token(token), url).context("build forgejo api client")
}