~ 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

The releases page shows how Cargo adds a crate

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:web/cargo-use
crates/knotless-serve/src/web/releases.rs+3−0
@@ -40,6 +40,8 @@ pub struct ReleaseView {
image: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
yanked: bool,
#[serde(rename = "crate", skip_serializing_if = "std::ops::Not::not")]
is_crate: bool,
#[serde(skip_serializing_if = "Option::is_none")]
commit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -210,6 +212,7 @@ pub(super) async fn list_releases(
.as_deref()
.map(|n| markdown(n, &base, &base, "", None)),
image: image(&r),
is_crate: r.cargo.is_some(),
uri,
package: r.package,
version: r.version,
crates/knotless-serve/tests/web.rs+5−0
@@ -3534,6 +3534,11 @@ fn a_cargo_registry() {
);
let everything = site.json(&format!("{here}/api/v1/crates"));
assert_eq!(everything["meta"]["total"], 3);
// The releases page knows each for a crate.
let repo = format!("at://{did}/dev.knotless.repo/crates");
let listed = site.json(&xrpc("listReleases", &[("repo", &repo)]));
let listed = listed["releases"].as_array().unwrap();
assert!(listed.iter().all(|r| r["crate"] == true), "{listed:?}");
// A file whose bytes are not what its record says is not sent, and a
// body too large is refused as it is read.
docs/design/packages.md+3−1
@@ -335,7 +335,9 @@ A Releases tab, with how many releases there are, opens the releases
page: every version of every package, newest first, each with its
notes, the commit it was built from and its files, each with its size
and SHA-256. An image shows the `docker pull` command for it rather than
its blobs.
its blobs, and a crate the lines naming its repository's
[registry](#crates) in `.cargo/config.toml` and the `cargo add` that
adds it, rather than its `.crate`.
Each file's name links to download it, from
`/<owner>/<repo>/releases/download/<package>/<version>/<file>`.
docs/reference/api.md+1−1
@@ -588,7 +588,7 @@ name it has now.
| `dev.knotless.searchOwners?q=` | the owners of repositories whose handle holds `q` (above) |
| `dev.knotless.getActorRepos?actor=` | an account's public repositories, each with the `language` most of it is written in: from the index while a feed is followed, else as its PDS lists them; to the account itself, asking, its private ones too, each `private`, and to a member asking those it was proven a member of; asked as the account itself, apart, as `shared`, the private repositories of others it was proven a member of |
| `dev.knotless.getRepo?repo=` | a repository: its owner, description, `links` (only http and https ones), `topics`, default branch, counts, languages, `collaborators` (the first twenty its record names, besides its owner) and `collaboratorCount`, `private` if it is, `archivedAt` if its owner archived it, and `site` if it has a website this server serves: its `url`, `deployedAt`, the `commit` its deployer named, and its `check` only when that check is from a runner the repository names; with an asker, `viewer`: its `star`, a `transfer` offered to it, the `labels` it may put on the repository's pulls and issues, and `admin: true` when it may change the repository's settings, as its owner or an admin who joined the organization that owns it |
| `dev.knotless.listReleases?repo=&package=&limit=&cursor=` | its releases ([packages.md](../design/packages.md#on-the-pages)), newest first, 30 a page by default, of one `package` if given: each with `latest` when it is its package's latest, `yanked` when its owner withdrew it, `image` when its files are an OCI layout's, its notes as Markdown and as `html`, and its files' names, sizes and SHA-256; and every package it publishes, by name, with its latest version, how many releases it has and whether it is an image. None for a private repository or without an index |
| `dev.knotless.listReleases?repo=&package=&limit=&cursor=` | its releases ([packages.md](../design/packages.md#on-the-pages)), newest first, 30 a page by default, of one `package` if given: each with `latest` when it is its package's latest, `yanked` when its owner withdrew it, `image` when its files are an OCI layout's, `crate` when it is a crate of the repository's Cargo registry, its notes as Markdown and as `html`, and its files' names, sizes and SHA-256; and every package it publishes, by name, with its latest version, how many releases it has and whether it is an image. None for a private repository or without an index |
| `dev.knotless.getTree?repo=&ref=&path=` | a folder, its README and the newest checks of its last commit |
| `dev.knotless.listPaths?repo=&ref=&limit=` | every folder and file at a revision, the shallowest first, at most 50,000, and `truncated` when the deepest were left out: for Go to file |
| `dev.knotless.getLastCommits?repo=&ref=&path=` | the newest commit to change each entry of a folder, and at the root, how many commits the ref's history holds |
lexicons/dev/knotless/defs.json+1−0
@@ -344,6 +344,7 @@
"latest": { "type": "boolean", "description": "True for its package's latest version: the highest with no pre-release part that is not yanked." },
"image": { "type": "boolean", "description": "True when its files are a container image's OCI layout: an `index.json` beside blobs named `sha256-<hex>`, pulled by `docker pull` rather than downloaded." },
"yanked": { "type": "boolean", "description": "True when its owner withdrew it, by a dev.knotless.releaseYank record: its files can still be read, but it is no one's latest." },
"crate": { "type": "boolean", "description": "True when it is a crate, added with Cargo from its repository's registry, at `/cargo/<owner's DID>/<repository's key>/`, rather than downloaded." },
"commit": { "type": "string", "description": "The hex object ID of the commit its files were built from, if its record names one." },
"check": { "type": "string", "format": "at-uri", "description": "The dev.knotless.check of the run that built it, if its record names one." },
"notes": { "type": "string", "description": "What changed since the version before, in Markdown, as its record says." },
web/src/lib/organisms/ReleaseCard.svelte+14−3
@@ -2,7 +2,7 @@
import type { ReleaseView } from '#lib/lexicons/dev/knotless/defs.ts';
import { page } from '$app/state';
import { ago, plural, repoHref, size } from '#lib/format.ts';
import { downloadHref, pullCommand, releaseAnchor } from '#lib/releases.ts';
import { cargoUse, downloadHref, pullCommand, releaseAnchor } from '#lib/releases.ts';
import Sha from '#lib/atoms/Sha.svelte';
import Time from '#lib/atoms/Time.svelte';
import Command from '#lib/molecules/Command.svelte';
@@ -14,12 +14,15 @@
* commit, its notes, and its files, each with its size and SHA-256 and
* linked to download;
* or, for an image, the
* command that pulls it rather than its blobs. A shared step's links to
* the step's page.
* command that pulls it rather than its blobs, and for a crate, how
* Cargo adds it. A shared step's links to the step's page.
*/
let { owner, name, release: r }: { owner: string; name: string; release: ReleaseView } = $props();
const step = $derived(stepOf(r.package));
const bytes = $derived(r.files.reduce((n, f) => n + f.size, 0));
const cargo = $derived(
r.crate ? cargoUse(page.url.origin, r.uri, name, r.package, r.version) : undefined
);
</script>
<article class="box release" id={releaseAnchor(r)}>
@@ -43,6 +46,14 @@
An image: {plural(r.files.length - 1, 'blob', 'blobs')} and its index, {size(bytes)} in all.
</p>
</div>
{:else if cargo}
<div class="release-pull">
<Command text={cargo.config} />
<Command text={cargo.add} />
<p class="mute">
A crate. Add the registry to <code>.cargo/config.toml</code>, then add the crate with Cargo.
</p>
</div>
{:else}
<ul class="list release-files" aria-label="Files">
{#each r.files as f (f.name)}
web/src/lib/releases.ts+24−1
@@ -1,5 +1,5 @@
// What the pages say of a repository's releases: which is a pre-release,
// how an image is pulled, and where each is.
// how an image is pulled, how a crate is added, and where each is.
import { repoHref, urlPath } from '#lib/format.ts';
@@ -22,6 +22,29 @@ export function pullCommand(
return `docker pull ${host}/${name}:${version}`;
}
/**
* What a crate's release, `uri`, says to Cargo, served from `origin`: the
* lines of `.cargo/config.toml` naming its repository's registry, as
* `registry`, and the command that adds the crate from it. The registry is
* at the owner's DID and the repository's key, which open its record's
* URI and key, as names can change and Cargo keeps it in `Cargo.lock`.
*/
export function cargoUse(
origin: string,
uri: string,
registry: string,
pkg: string,
version: string
): { config: string; add: string } {
const [did, , rkey] = uri.replace(/^at:\/\//, '').split('/');
const key = rkey.split('.')[0];
const name = registry.replace(/[^A-Za-z0-9_-]/g, '-').replace(/^[^A-Za-z_]/, 'r$&');
return {
config: `[registries.${name}]\nindex = "sparse+${origin}/cargo/${did}/${key}/"`,
add: `cargo add ${pkg}@${version} --registry ${name}`
};
}
/** The id a release has on the releases page, for a link to land on it. */
export const releaseAnchor = (r: { package: string; version: string }) =>
`${r.package}@${r.version}`;