~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

A private repository's pusher's mirror takes in their pack as they push

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:serve/seed-pusher-mirror
crates/knotless-serve/src/gateway.rs+21−1
@@ -679,10 +679,14 @@ pub async fn push(
app.event("gateway.push", "", true, started.elapsed(), Some(&caller));
// By the owner it wrote for, whoever asked: what delegating led to,
// once a ref moved.
if report.iter().any(|(_, res)| res.is_ok()) {
let moved = report.iter().any(|(_, res)| res.is_ok());
if moved {
let owner = Some(remote.did());
app.event("gateway.served", "", true, started.elapsed(), owner);
}
if moved && remote.space.is_some() && !pack.is_empty() {
seed(&app, &caller, remote.did(), &snap, body.slice_ref(pack));
}
Ok(Json(PushResults {
results: report
.into_iter()
@@ -694,6 +698,22 @@ pub async fn push(
}))
}
/// Puts `pack`, as `caller` pushed it to `did`'s private repository
/// `snap`, in the mirror of it the pages keep for them, if they keep one:
/// aside, as the push is answered. Their next look then reads its records
/// and not this pack again from the PDS. Only what they sent goes in,
/// theirs to read already, so their mirror is still theirs alone
/// (`docs/design/access.md`). A pack whose bases that mirror lacks is not
/// taken in, and that look fetches it as before.
fn seed(app: &App, caller: &str, did: &str, snap: &Snapshot, pack: Bytes) {
let key = snap.key().expect("a writable repository exists");
let ctx = crate::login::ctx_for(app, caller);
let dir = knotless::mirror::path(&ctx, did, key);
if dir.join("HEAD").exists() {
tokio::spawn(async move { knotless::git::Git::new(dir).index_pack(&pack).await.ok() });
}
}
pub async fn commit(
State(app): State<Arc<App>>,
headers: HeaderMap,
crates/knotless-serve/tests/gateway.rs+30−0
@@ -805,12 +805,42 @@ fn private_repositories_through_a_gateway() {
0
);
// The collaborator reads it on the pages first, which keeps a mirror
// of it for them alone.
let cookie = session_cookie(&gw, &collab.account);
let at = format!(
"{}/xrpc/dev.knotless.getRepo?repo=at://{}/dev.knotless.repo/hidden",
gw.base, owner.account.did
);
let (status, .., shown) = looked(&at, Some(&cookie));
assert_eq!(status, 200, "{shown}");
let underscored = |did: &str| did.replace(':', "_");
let theirs = (gw.app.ctx.env.cache_dir.join("web"))
.join(underscored(&collab.account.did))
.join("mirrors")
.join(underscored(&owner.account.did));
let mut kept = std::fs::read_dir(&theirs)
.unwrap()
.map(|e| e.unwrap().path());
let mirror = kept.find(|p| p.extension().is_some_and(|x| x == "git"));
let mirror = mirror.expect("a mirror of it, theirs");
let clone = collab.path("clone");
collab.git(collab.dir.path(), &["clone", "-q", &url, "clone"]);
let sha = collab.commit_file(&clone, "theirs", "private work\n");
collab.git(&clone, &["push", "-q", "origin", "main"]);
let (_, refs) = owner.cli(&["repo", "refs", &url], b"");
assert_eq!(refs, format!("{sha}\trefs/heads/main"));
// What they pushed through the gateway is in their mirror before they
// look again.
let has = |git: &std::path::Path| {
let found = std::process::Command::new("git")
.args(["cat-file", "-e", &sha])
.current_dir(git)
.status();
found.is_ok_and(|s| s.success())
};
wait_until(|| has(&mirror));
let (_, members) = owner.cli(&["repo", "members", &url], b"");
assert_eq!(
members,
docs/design/access.md+7−4
@@ -239,10 +239,13 @@ sequenceDiagram
only to a member.
- What is found is that member's alone: its mirror kept under their cache,
and its entry under their DID, so nothing read for one viewer answers
another. Its pulls, issues and checks are read from the server's own
index of its space ([An index of every one read
here](#an-index-of-every-one-read-here)), and a check's steps and log
from the runner's repo in the space, as the member.
another. A pack a member pushes through the gateway goes into their own
mirror too, if they have one, as the push is answered, so their next
look reads its records but not that pack again: what they sent was
theirs to read already, and goes into no one else's. Its pulls, issues
and checks are read from the server's own index of its space ([An index
of every one read here](#an-index-of-every-one-read-here)), and a
check's steps and log from the runner's repo in the space, as the member.
- Membership is asked again, with a fresh credential, once the last answer
is a minute old; in between, the space is read as it was found then,
with no round trip to look for it again. Its mirror is synced again only
docs/design/gateway.md+5−1
@@ -163,7 +163,11 @@ as the owner, with the same
naming the caller, from its verified token, as the `pusher` of each claim and
ref it writes, unless the caller is the owner. It answers
`results`, a `{name, error?}` per ref: each update lands or is refused on
its own, and the client fails on the first one refused.
its own, and the client fails on the first one refused. Once a ref of a
private repository moved, the pack also goes into the caller's own mirror
of it, if the pages keep one for them, aside: their next look on the pages
then reads only its records
([access.md](access.md#reading-a-private-repository)).
## Runners landing a queue