~ 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

createCommit holds linear branches, and backfill reads redirects

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:serve/small-fixes
crates/knotless-run/src/lib.rs+14−1
@@ -356,6 +356,9 @@ fn count(flag: &str, what: &str, value: String) -> Result<usize, String> {
n.ok_or(format!("{flag} takes a number of {what}, at least 1"))
}
/// The most pulls a batch takes: a check record's `batch` holds no more.
const MAX_BATCH: usize = 64;
/// `value`, read as CPUs, as docker's `--cpus` takes them: more than none.
fn cpus(value: String) -> Result<String, String> {
let n: f64 = value.parse().unwrap_or(0.0);
@@ -400,7 +403,12 @@ pub fn parse_args(args: &[String]) -> Result<Options, String> {
"--trust" => opts.trust.push(value()?),
"--discover" => opts.discover = true,
"--queue" => opts.queue = true,
"--batch" => opts.batch = count(flag, "pulls", value()?)?,
"--batch" => {
opts.batch = count(flag, "pulls", value()?)?;
if opts.batch > MAX_BATCH {
return Err(format!("--batch is {MAX_BATCH} pulls at most"));
}
}
"--depth" => {
opts.depth = count(flag, "batches", value()?)?;
if opts.depth > 2 {
@@ -4659,6 +4667,11 @@ mod tests {
assert_eq!((opts.queue, opts.batch, opts.depth), (true, 3, 2));
let opts = args("a/b").unwrap();
assert_eq!((opts.queue, opts.batch, opts.depth), (false, 8, 2));
assert_eq!(args("--batch 64 a/b").unwrap().batch, 64);
assert_eq!(
args("--batch 65 a/b").unwrap_err(),
"--batch is 64 pulls at most"
);
assert_eq!(args("--depth 1 a/b").unwrap().depth, 1);
assert_eq!(
args("--depth 0 a/b").unwrap_err(),
crates/knotless-serve/src/gateway.rs+7−0
@@ -809,6 +809,13 @@ async fn attempt(
dst: refname.clone(),
force: true,
};
// What a push of this tip would be refused, a commit is: a new
// branch under a linear rule takes no merge commits from its base.
let owner = remote.did();
let held = gateway::refusal(&mirror.git, &snap, &update, owner, caller).await;
if let Some(why) = held.map_err(AppError::internal)? {
return Err(forbidden(format!("{refname}: {why}")));
}
let report = push::push_by(
&app.ctx,
&remote,
crates/knotless-serve/src/lib.rs+6−4
@@ -9,10 +9,11 @@
//! - a JSON and raw API at `/api/<handle or did>/<name>/...` for agents that
//! want a file, a tree, a log or a diff without a clone.
//!
//! It holds no state of its own: every mirror is a cache of the owner's
//! records, revalidated against them (one round trip) when older than the
//! TTL, or kept warm by the firehose or Jetstream. Every object it serves is named by its
//! hash, so a client can check it.
//! Everything it holds is a cache of the owners' records, which stay the
//! truth: each mirror is revalidated against them (one round trip) when
//! older than the TTL, or kept warm by the firehose or Jetstream, and the
//! index (a SQLite file) is built from them and can be rebuilt by a backfill.
//! Every object it serves is named by its hash, so a client can check it.
pub mod accounts;
mod api;
@@ -821,6 +822,7 @@ fn indexed_collections() -> impl Iterator<Item = &'static str> {
knotless::records::nsid::REPO,
knotless::records::nsid::REF,
knotless::records::nsid::CLAIM,
knotless::records::nsid::REDIRECT,
knotless::records::nsid::CHECK,
knotless::records::nsid::MINUTES,
knotless::sealed::KEY_NSID,
crates/knotless-serve/tests/gateway.rs+10−3
@@ -290,6 +290,16 @@ fn protected_branches_hold_collaborators() {
owner.cli(&["protect", &url, "release"], b"");
let err = refused(&collab, &clone, "main:refs/heads/release").unwrap();
assert!(err.contains("no merge commits"), "{err}");
// Nor does a commit, made on a base with a merge in its history.
let merged = collab.git(&clone, &["rev-parse", "HEAD"]);
let note = collab.path("note");
std::fs::write(&note, "n\n").unwrap();
let path = note.to_string_lossy();
let on_merge = [
"commit", &url, "-b", "release", "-m", "n", "--base", &merged, "--put", "n", &path,
];
let (done, _, err) = collab.knotless(&on_merge, b"");
assert!(!done && err.contains("no merge commits"), "{err}");
collab.git(&clone, &["reset", "-q", "--hard", &base]);
let err = refused(&collab, &clone, "+HEAD~1:main").unwrap();
assert!(err.contains("only moves forward"), "{err}");
@@ -333,9 +343,6 @@ fn protected_branches_hold_collaborators() {
);
let err = refused(&collab, &clone, "main").unwrap();
assert!(err.contains("only through the merge queue"), "{err}");
let note = collab.path("note");
std::fs::write(&note, "n\n").unwrap();
let path = note.to_string_lossy();
let commit = ["commit", &url, "-b", "main", "-m", "n", "--put", "n", &path];
let (done, _, err) = collab.knotless(&commit, b"");
assert!(
docs/design/gateway.md+7−4
@@ -259,10 +259,13 @@ not, whose commit's message ends with a pull's AT-URI, as every merge's
does (`merge gate: <why>` on an unprotected branch); a commit that says no
such thing passes, as the collaborator could push it anyway.
Commits through `createCommit` build on the tip with one parent, so they
pass the rest; into a `queue` branch they are refused from a collaborator
before they are batched, 403 `<branch> takes pulls only through the merge
queue: commit to another branch and open a pull`, and into a branch that
Commits through `createCommit` build on the tip with one parent, so on an
existing branch they pass the rest; a new branch under a linear rule is
refused as a push of it would be, 403 `protected branch: no merge commits;
squash or rebase`, when its `base` has merge commits in its history. Into a
`queue` branch they are refused from a collaborator before they are
batched, 403 `<branch> takes pulls only through the merge queue: commit to
another branch and open a pull`, and into a branch that
asks for reviews, 403 `<branch> takes changes only through reviewed pulls:
commit to another branch and open a pull`. Pulls merge into a linear branch
[squashed](collaboration.md#merging).
docs/reference/cli.md+3−2
@@ -471,7 +471,7 @@ add` make it one, and invite it, which `--discover` reads.
| `--pulls` | off | also run open pulls, at their head merged with their base, without secrets; takes `docker` |
| `--pulls-only <handle>/<name>` | none | watch that repository for its open pulls alone, never its pushes or its merge queue, which another runner runs; as there, a queued pull does not run on its own, and the rest run again as their head moves, not their base; repeatable; takes `--pulls` |
| `--queue` | off | land the repositories' [merge queues](../design/merge-queue.md) where its account may push, or where they name it and no runner that may ([Who lands a queue](../design/merge-queue.md#who-lands-a-queue)); takes `docker` |
| `--batch <n>` | 8 | with `--queue`, the most pulls in a batch |
| `--batch <n>` | 8 | with `--queue`, the most pulls in a batch, 64 at most (a check record's `batch` holds no more) |
| `--depth <n>` | 2 | with `--queue`, 2 checks the next batch on top of the one being checked; 1 turns that off; nothing else |
| `--notice <secs>` | 30 | with `--queue`, how often the owner's and collaborators' queue records are read |
| `--review-wait <secs>` | 1800 | with `--queue`, how long a queued pull waits for a review of its head from its branch's [gates](../design/collaboration.md#gates) before it is ejected |
@@ -482,7 +482,8 @@ and `--pids` take at least 1, as in `--batch takes
a number of pulls, at least 1` and `--notice takes a number of seconds, at
least 1`. `--depth 0` is refused with `--depth takes a number of batches,
at least 1`, and more than 2 with `--depth is 1 or 2: one batch at most
runs ahead`. `--pulls` and
runs ahead`. `--batch` above 64 is refused with `--batch is 64 pulls at
most`. `--pulls` and
`--queue` refuse `--executor shell` (`--pulls and --queue run code anyone
can open a pull with: they take --executor docker`), and so do `--cpus`,
`--memory`, `--pids`, `--network`, `--runtime`, `--trusted-runtime` and