attest

tcmts.cli.attest.attest · #kua5d3qo · term · runs on the server

uses Buffer

tcm lib attest: write a review, an advisory or (a release's author's) a yank, about what, to the signed-in account's repository.

import { atprotoSession } from "#codebase/tcmts/atproto";
import { ANNOUNCE_SCOPE } from "#codebase/tcmts/cli/announce";
import { attestationSubject, writeAttestation } from "#codebase/tcmts/cli/attest";
import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { bold, dim, green } from "#codebase/tcmts/cli/style";
import { UsageError } from "#codebase/tcmts/cli/ui";
import { shortHash } from "#codebase/tcmts/core/hash";
import { ATTESTATION_KINDS, SEVERITIES } from "#codebase/tcmts/records";
import type { AttestationKind } from "#codebase/tcmts/records";

export async function attest(
  cb: Codebase,
  m: {
    what: string;
    kind: string;
    message: string | undefined;
    severity: string | undefined;
    "fixed-in": string | undefined;
    as: string | undefined;
  },
): Promise<void> {
  if (!(m.kind in ATTESTATION_KINDS))
    throw new UsageError(`'${m.kind}' isn't a kind of attestation: review, advisory or yank`);
  const kind = m.kind as AttestationKind;
  const severity = SEVERITIES.find((s) => s === m.severity);
  if (kind === "advisory" && !severity)
    throw new UsageError(`an advisory says how bad it is: --severity ${SEVERITIES.join("|")}`);
  if (kind !== "advisory" && (m.severity || m["fixed-in"]))
    throw new UsageError("--severity and --fixed-in are an advisory's");
  if (kind !== "review" && !m.message?.trim())
    throw new UsageError(
      `a${kind === "advisory" ? "n advisory" : " yank"} says what it's about: -m <text>`,
    );
  // (as its lexicon says)
  if (Buffer.byteLength(m.message?.trim() ?? "") > 30000)
    throw new UsageError("-m: an attestation says at most 30000 bytes");
  if ((m["fixed-in"]?.length ?? 0) > 64)
    throw new UsageError("--fixed-in: a version, at most 64 characters");
  const subject = await attestationSubject(cb, m.what);
  const session = await atprotoSession({
    who: m.as,
    scope: ANNOUNCE_SCOPE,
    why: `attesting writes a record to your AT Protocol repository`,
  });
  if (kind === "yank" && subject.author && subject.author !== session.did)
    throw new UsageError(
      `only its author can yank ${subject.release ?? "a release"}: they're ${subject.author}, and you're signed in as ${session.handle ? `@${session.handle}` : session.did} (an advisory says what's wrong with it)`,
    );
  const uri = await writeAttestation(session, {
    kind,
    subject: kind === "yank" && !subject.author ? { ...subject, author: session.did } : subject,
    body: m.message?.trim() ?? "",
    ...(severity ? { severity } : {}),
    ...(m["fixed-in"] ? { fixedIn: m["fixed-in"] } : {}),
    createdAt: new Date().toISOString(),
  });
  const verb = { review: "reviewed", advisory: "wrote an advisory about", yank: "yanked" }[kind];
  console.log(
    `${green(verb)} ${bold(subject.release ?? shortHash(subject.namespace))} ${dim(`(${shortHash(subject.namespace)})`)} as ${session.handle ? `@${session.handle}` : session.did}: ${uri}`,
  );
}

uses

used by