fix(#1198): create internal/knowledge as public, patch existing private repos
This commit is contained in:
parent
69a48f086c
commit
cd1f77b640
1 changed files with 46 additions and 36 deletions
|
|
@ -55,10 +55,10 @@ const SHARED_ORG: &str = "internal";
|
|||
/// The shared docs repo inside `SHARED_ORG`. Cloneable by every agent
|
||||
/// at `{FORGE_HTTP}/internal/docs.git`.
|
||||
const SHARED_DOCS_REPO: &str = "docs";
|
||||
/// The hive-wide knowledge repo inside `SHARED_ORG`. Bind-mounted
|
||||
/// read-only into every container at `/knowledge`. Agents
|
||||
/// contribute by forking + opening PRs; direct writes to main are
|
||||
/// not granted. See `hive-c0re/src/knowledge.rs`.
|
||||
/// The hive-wide knowledge repo inside `SHARED_ORG`. Public — agents
|
||||
/// can fork it and open PRs without explicit collaborator grants.
|
||||
/// Bind-mounted read-only into every container at `/knowledge`.
|
||||
/// See `hive-c0re/src/knowledge.rs`.
|
||||
const KNOWLEDGE_REPO: &str = crate::knowledge::REPO;
|
||||
/// Forgejo orgs hive-c0re ensures on startup. The meta repo lives at
|
||||
/// `core/meta` (the `core` user's own namespace — no org needed).
|
||||
|
|
@ -470,6 +470,37 @@ fn repo_body(name: &str) -> String {
|
|||
format!(r#"{{"name":"{name}","auto_init":false,"private":true,"default_branch":"main"}}"#)
|
||||
}
|
||||
|
||||
/// JSON body for a public, empty repo defaulting to `main`.
|
||||
fn repo_body_public(name: &str) -> String {
|
||||
format!(r#"{{"name":"{name}","auto_init":false,"private":false,"default_branch":"main"}}"#)
|
||||
}
|
||||
|
||||
/// Set an existing repo to public visibility. No-op if the repo is
|
||||
/// already public. Used for `internal/knowledge` which may have been
|
||||
/// created as private on an older deployment.
|
||||
async fn set_repo_public(owner: &str, repo: &str, token: &str) -> Result<()> {
|
||||
let url = format!("{FORGE_HTTP}/api/v1/repos/{owner}/{repo}");
|
||||
let status = forge_http(reqwest::Method::PATCH, &url, token, r#"{"private":false}"#).await?;
|
||||
match status.as_u16() {
|
||||
200 => {
|
||||
tracing::debug!(%owner, %repo, "forge: repo set to public");
|
||||
Ok(())
|
||||
}
|
||||
other => anyhow::bail!("PATCH {owner}/{repo} (set public) returned HTTP {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create `name` inside org `org` as a public repo. Idempotent.
|
||||
async fn ensure_org_repo_public(org: &str, name: &str, token: &str) -> Result<()> {
|
||||
create_repo(
|
||||
&format!("{FORGE_HTTP}/api/v1/orgs/{org}/repos"),
|
||||
&repo_body_public(name),
|
||||
token,
|
||||
&format!("{org}/{name}"),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// POST a repo-creation request to `url` and fold "already exists"
|
||||
/// (HTTP 409 / 422) into success. `label` is `<owner>/<name>` — purely
|
||||
/// for log + error context.
|
||||
|
|
@ -611,32 +642,17 @@ pub async fn shared_docs_access(name: &str, core_token: &str) -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Ensure the `internal/knowledge` repo exists. Called once at
|
||||
/// startup after `ensure_org(SHARED_ORG)`. Idempotent — `ensure_org_repo`
|
||||
/// treats 409 as success.
|
||||
/// Ensure the `internal/knowledge` repo exists and is public.
|
||||
/// Called once at startup after `ensure_org(SHARED_ORG)`. Idempotent.
|
||||
///
|
||||
/// The repo is created as public so any agent with a forge account can
|
||||
/// fork it and open PRs to contribute. Existing deployments that ended
|
||||
/// up with a private repo are patched to public on the next hive-c0re
|
||||
/// startup via `set_repo_public`.
|
||||
pub async fn ensure_knowledge_repo(core_token: &str) -> Result<()> {
|
||||
ensure_org_repo(SHARED_ORG, KNOWLEDGE_REPO, core_token).await
|
||||
}
|
||||
|
||||
/// Grant agent `name` read-only collaborator access to
|
||||
/// `internal/knowledge`. Agents read documents from the bind-mounted
|
||||
/// clone and contribute by forking + opening PRs — no write to main.
|
||||
/// Idempotent: HTTP 204 (already a collaborator) is treated as success.
|
||||
pub async fn knowledge_access(name: &str, core_token: &str) -> Result<()> {
|
||||
let url = format!(
|
||||
"{FORGE_HTTP}/api/v1/repos/{SHARED_ORG}/{KNOWLEDGE_REPO}/collaborators/{name}"
|
||||
);
|
||||
let body = r#"{"permission":"read"}"#;
|
||||
let status = forge_http(reqwest::Method::PUT, &url, core_token, body).await?;
|
||||
match status.as_u16() {
|
||||
204 => {
|
||||
tracing::info!(%name, "forge: granted knowledge read access");
|
||||
Ok(())
|
||||
}
|
||||
other => anyhow::bail!(
|
||||
"PUT {SHARED_ORG}/{KNOWLEDGE_REPO}/collaborators/{name} returned HTTP {other}"
|
||||
),
|
||||
}
|
||||
ensure_org_repo_public(SHARED_ORG, KNOWLEDGE_REPO, core_token).await?;
|
||||
// Ensure public even if the repo already existed as private (older deployment).
|
||||
set_repo_public(SHARED_ORG, KNOWLEDGE_REPO, core_token).await
|
||||
}
|
||||
|
||||
/// Grant agent `name` read-only collaborator access to `core/meta` on
|
||||
|
|
@ -818,13 +834,7 @@ pub async fn sync_agent(name: &str, core_token: Option<&str>) {
|
|||
{
|
||||
tracing::warn!(%name, error = ?e, "forge: shared_docs_access failed");
|
||||
}
|
||||
// Grant read-only access to internal/knowledge so the agent can
|
||||
// read the bind-mounted knowledge repo and fork it to open PRs.
|
||||
if let Some(token) = core_token
|
||||
&& let Err(e) = knowledge_access(name, token).await
|
||||
{
|
||||
tracing::warn!(%name, error = ?e, "forge: knowledge_access failed");
|
||||
}
|
||||
// internal/knowledge is public — no per-agent collaborator grant needed.
|
||||
}
|
||||
|
||||
/// Sweep every existing container (manager + sub-agents) and ensure
|
||||
|
|
|
|||
Loading…
Reference in a new issue