Watch
0
0
Fork
You've already forked hyperhive
0

deploy the closure the swarm declares, read no forge head, drop the update badge

This commit is contained in:
atlas 2026-11-01 23:56:40 +01:00 • committed by mara
commit 47c2acb640

View file

@ -4,18 +4,13 @@
//! `internal/docs`, are swarm-controller's. Full design:
//! `docs/integrations/forge.md`.
mod internal_meta;
mod reader;
pub(crate) use internal_meta::internal_meta_source;
pub use reader::reader_token;
use std::future::IntoFuture;
use std::sync::OnceLock;
use std::time::Duration;
use anyhow::{Context, Result};
use forgejo_api::{Auth, Forgejo};
use url::Url;
const FORGE_CONTAINER: &str = "hive-forge";
@ -88,15 +83,6 @@ fn basic_auth_header(login: &str, token: &str) -> String {
format!("Authorization: Basic {basic}")
}
/// Forgejo org grouping every agent's config repo. Each agent is a **write collaborator
/// on its own** `agent-configs/<name>` repo — the editable PR surface it
/// pushes config-change branches to. `main`'s branch protection is
/// swarm-controller's: merge and approval allowlisted to the `operators` team,
/// so the agent can't push `main` or self-merge, and an operator's merge
/// deploys through swarm-controller. The repos remain private, so an agent
/// still can't reach *another* agent's config. hive-c0re only reads from it.
pub(crate) const CONFIG_ORG: &str = "agent-configs";
/// Probe whether `hive-forge` exists as a nixos-container. Cheap —
/// `nixos-container list` is just a directory scan in /etc. Routed
/// through hive-priv: `nixos-container` needs root, and hive-c0re runs
@ -108,70 +94,9 @@ pub async fn is_present() -> bool {
stdout.lines().any(|l| l.trim() == FORGE_CONTAINER)
}
/// Typed Forgejo API client for the local forge ([`forge_http_base()`]),
/// authenticated as `token`. All Forgejo API calls go through clients built
/// here — one place for the base URL and auth. The token is passed per call;
/// the base URL is parsed once. Failures surface as
/// `forgejo_api::ForgejoError`, whose Display carries the HTTP status
/// and the API's error message (e.g. the validation reason on a 422).
pub(crate) fn api(token: &str) -> Result<Forgejo> {
static URL: OnceLock<Url> = OnceLock::new();
let url = 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")
}
/// Upper bound on one Forgejo API request. `forgejo-api` takes no
/// caller-built client, so the request itself has no timeout; every request
/// in this module is wrapped in [`bounded`].
const API_TIMEOUT: Duration = Duration::from_secs(10);
/// Await one Forgejo API request for at most [`API_TIMEOUT`].
///
/// The outer `Err` is the deadline, naming `op`; the inner `Result` is the
/// request's own outcome, left to the caller so it can still match on the
/// error (`409 already exists`, `401`, ...).
async fn bounded<F, T, E>(op: &str, request: F) -> Result<std::result::Result<T, E>>
where
F: IntoFuture<Output = std::result::Result<T, E>>,
{
tokio::time::timeout(API_TIMEOUT, request)
.await
.map_err(|_| anyhow::anyhow!("{op}: no answer from the forge within {API_TIMEOUT:?}"))
}
#[cfg(test)]
mod tests {
use super::{API_TIMEOUT, bounded, git_url_with_base};
/// A forge that never answers surfaces as an error naming the operation,
/// at exactly [`API_TIMEOUT`] — not as a hang.
#[tokio::test(start_paused = true)]
async fn a_request_the_forge_never_answers_errors_at_the_deadline() {
let started = tokio::time::Instant::now();
let err = bounded(
"pending request",
std::future::pending::<Result<(), forgejo_api::ForgejoError>>(),
)
.await
.expect_err("a pending request must hit the deadline");
assert_eq!(started.elapsed(), API_TIMEOUT);
assert!(err.to_string().contains("pending request"), "{err}");
}
/// The request's own result passes through for the caller to classify.
#[tokio::test(start_paused = true)]
async fn an_answered_request_keeps_its_own_result() {
let ok = bounded("op", async { Ok::<_, ()>(7) })
.await
.expect("in time");
assert_eq!(ok, Ok(7));
let failed = bounded("op", async { Err::<(), _>("409") })
.await
.expect("in time");
assert_eq!(failed, Err("409"));
}
use super::git_url_with_base;
/// The remote carries **no credential**. `argv` is world-readable through
/// `/proc/<pid>/cmdline`, so a token spliced in here would be published to