Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
17 kB · 472 lines
Shell
at icy/tmrrpn
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473#!/bin/shset -eu
: "${KNOT_URL:?KNOT_URL must be set}": "${KNOT_HOSTNAME:?KNOT_HOSTNAME must be set}": "${KNOT2_URL:?KNOT2_URL must be set}": "${KNOT2_HOSTNAME:?KNOT2_HOSTNAME must be set}": "${PDS_HOSTNAME:?PDS_HOSTNAME must be set}"
SHARED_DIR="${SHARED_DIR:-/shared}"
. /scripts/lib.sh
OWNER_DID=$(cat "${SHARED_DIR}/owner-did")OWNER_JWT=$(login "$OWNER_DID")
BOB_DID=$(resolve_handle "bob.${PDS_HOSTNAME}")BOB_JWT=$(login "$BOB_DID")
CHARLIE_DID=$(resolve_handle "charlie.${PDS_HOSTNAME}")DAVID_DID=$(resolve_handle "david.${PDS_HOSTNAME}")
CORE_SOURCE=https://tangled.org/boltless.me/core.archiveCORE_SOURCE_DIRECT=https://knot1.tangled.sh/did:plc:ioighzh4jnrhybayw65gfbia
# every record this script seeds, and who authors itREPO_CORE=at://$OWNER_DID/sh.tangled.repo/coreREPO_CORE_KNOT2=at://$OWNER_DID/sh.tangled.repo/core.knot2REPO_EMPTY=at://$BOB_DID/sh.tangled.repo/empty-repo
ISSUE_1=at://$OWNER_DID/sh.tangled.repo.issue/3lzg6f3aia222ISSUE_2=at://$OWNER_DID/sh.tangled.repo.issue/3lzg6guhjy222ISSUE_3=at://$OWNER_DID/sh.tangled.repo.issue/3lzg6inolq222
STATE_1=at://$OWNER_DID/sh.tangled.repo.issue.state/3lzg6kgvni222
PULL_1=at://$OWNER_DID/sh.tangled.repo.pull/3lzg7tkxvq222PULL_2=at://$OWNER_DID/sh.tangled.repo.pull/3ms6rk5d6gk22
STRING_CODE=at://$OWNER_DID/sh.tangled.string/3lzg6ma4pa222STRING_MARKDOWN=at://$OWNER_DID/sh.tangled.string/3lzg6nzdqy222STRING_TEXT=at://$BOB_DID/sh.tangled.string/3lzg6psksq222
COMMENT_ISSUE=at://$BOB_DID/sh.tangled.feed.comment/3lzg6rlrui222COMMENT_ISSUE_REPLY=at://$OWNER_DID/sh.tangled.feed.comment/3lzg6teywa222COMMENT_STRING=at://$BOB_DID/sh.tangled.feed.comment/3lzg6v67xy222COMMENT_STRING_REPLY=at://$OWNER_DID/sh.tangled.feed.comment/3lzg6wxgzq222
REACT_ISSUE_A=at://$BOB_DID/sh.tangled.feed.reaction/3lzg6yqo3i222REACT_ISSUE_B=at://$OWNER_DID/sh.tangled.feed.reaction/3lzg72jv5a222REACT_COMMENT_A=at://$OWNER_DID/sh.tangled.feed.reaction/3lzg74d46y222REACT_COMMENT_B=at://$OWNER_DID/sh.tangled.feed.reaction/3lzg764daq222
FOLLOW_ALICE_BOB=at://$OWNER_DID/sh.tangled.graph.follow/3lzg77vkci222FOLLOW_ALICE_CHARLIE=at://$OWNER_DID/sh.tangled.graph.follow/3lzg7borea222FOLLOW_BOB_ALICE=at://$BOB_DID/sh.tangled.graph.follow/3lzg7dhyfy222FOLLOW_CHARLIE_ALICE=at://$CHARLIE_DID/sh.tangled.graph.follow/3lzg7fb7hq222FOLLOW_DAVID_ALICE=at://$DAVID_DID/sh.tangled.graph.follow/3lzg7h2gji222FOLLOW_DAVID_BOB=at://$DAVID_DID/sh.tangled.graph.follow/3lzg7itnla222
# a vouch's subject is its rkey, not a record fieldVOUCH_ALICE_BOB=at://$OWNER_DID/sh.tangled.graph.vouch/$BOB_DIDVOUCH_BOB_CHARLIE=at://$BOB_DID/sh.tangled.graph.vouch/$CHARLIE_DIDVOUCH_CHARLIE_DAVID=at://$CHARLIE_DID/sh.tangled.graph.vouch/$DAVID_DIDVOUCH_DAVID_ALICE=at://$DAVID_DID/sh.tangled.graph.vouch/$OWNER_DIDVOUCH_BOB_DAVID=at://$BOB_DID/sh.tangled.graph.vouch/$DAVID_DID
# knot2( KNOT_HOSTNAME=$KNOT2_HOSTNAME KNOT_URL=$KNOT2_URL
# idempotent: the knot no-ops a subject that is already a member or an admin for did in "$BOB_DID" "$CHARLIE_DID" "$DAVID_DID"; do curl -fsS -o /dev/null -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(service_token "$OWNER_JWT" sh.tangled.knot.addMember)" \ -d "$(jq -nc --arg subject "$did" '{subject:$subject}')" \ "${KNOT_URL}/xrpc/sh.tangled.knot.addMember" printf '[knot2] member %s\n' "$did" >&2 done
# same upstream as the go knot's core, via $CORE_SOURCE_DIRECT for the reason noted # there. a fork adopts the source's object format, so this lands as sha1 despite # knot2 defaulting to sha256. KNOT2_CORE_DID=$(curl -fsS \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(service_token "$OWNER_JWT" sh.tangled.repo.create)" \ -d "$(jq -nc --arg source "$CORE_SOURCE_DIRECT" \ '{rkey:"core.knot2", name:"core.knot2", defaultBranch:"master", source:$source}')" \ "${KNOT_URL}/xrpc/sh.tangled.repo.create" | jq -er '.repoDid')
printf '[knot2] core.knot2 = %s\n' "$KNOT2_CORE_DID" >&2
put_record "$REPO_CORE_KNOT2" "$(jq -nc \ --arg knot "$KNOT_HOSTNAME" \ --arg repoDid "$KNOT2_CORE_DID" \ --arg source "$CORE_SOURCE_DIRECT" \ --arg createdAt "$CREATED_AT" \ '{knot:$knot, name:"core.knot2", repoDid:$repoDid, source:$source, createdAt:$createdAt}')" >/dev/null)
# --- repo fixtures ---
REPO_DID=$(curl -fsS \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(service_token "$OWNER_JWT" sh.tangled.repo.create)" \ -d "$(jq -nc --arg source "$CORE_SOURCE" \ '{rkey:"core", name:"core", defaultBranch:"master", source:$source}')" \ "${KNOT_URL}/xrpc/sh.tangled.repo.create" | jq -er '.repoDid')
printf '[knot] core = %s\n' "$REPO_DID" >&2
put_record "$REPO_CORE" "$(jq -nc \ --arg knot "$KNOT_HOSTNAME" \ --arg repoDid "$REPO_DID" \ --arg source "$CORE_SOURCE" \ --arg createdAt "$CREATED_AT" \ '{knot:$knot, name:"core", repoDid:$repoDid, source:$source, createdAt:$createdAt}')" >/dev/null
# bob needs server:member on the knot before he may create a repo therecurl -fsS -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(service_token "$OWNER_JWT" sh.tangled.knot.addMember)" \ -d "$(jq -nc --arg subject "$BOB_DID" '{subject:$subject}')" \ "${KNOT_URL}/xrpc/sh.tangled.knot.addMember"
EMPTY_REPO_DID=$(curl -fsS \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(service_token "$BOB_JWT" sh.tangled.repo.create)" \ -d '{"rkey":"empty-repo", "name":"empty-repo", "defaultBranch":"master"}' \ "${KNOT_URL}/xrpc/sh.tangled.repo.create" | jq -er '.repoDid')
printf '[knot] empty-repo = %s\n' "$EMPTY_REPO_DID" >&2
put_record "$REPO_EMPTY" "$(jq -nc \ --arg knot "$KNOT_HOSTNAME" \ --arg repoDid "$EMPTY_REPO_DID" \ --arg createdAt "$CREATED_AT" \ '{knot:$knot, name:"empty-repo", repoDid:$repoDid, createdAt:$createdAt}')" >/dev/null
# --- issue fixtures ---
put_record "$ISSUE_1" "$(jq -nc \ --arg repo "$REPO_DID" \ --arg title 'Repo tree does not render symlinks' \ --arg body "$(cat <<'MARKDOWN'Symlinked entries in the file tree show up with a blank size and a link that404s. Expected them to render with the target path, the way a plain file does.
Reproduced on the default branch with `contrib/` present.MARKDOWN)" \ --arg createdAt '2025-09-22T10:14:35Z' \ '{repo:$repo, title:$title, body:$body, createdAt:$createdAt}')" >/dev/null
ISSUE_2_CID=$(put_record "$ISSUE_2" "$(jq -nc \ --arg repo "$REPO_DID" \ --arg title 'Document the knot bootstrap flow' \ --arg body "$(cat <<'MARKDOWN'## What's missing
> blah blah blah
```I won't let claude to yap some nonsense here.```
---
something _something_MARKDOWN)" \ --arg createdAt '2025-09-22T10:15:35Z' \ '{repo:$repo, title:$title, body:$body, createdAt:$createdAt}')")
put_record "$ISSUE_3" "$(jq -nc \ --arg repo "$REPO_DID" \ --arg title 'Clone over ssh fails on first push' \ --arg body "$(cat <<'MARKDOWN'Fresh clone, first push to a branch nobody has pushed before, and the remotehangs up:
```$ git push origin sl/my-branchEnumerating objects: 12, done.remote: error: cannot lock ref 'refs/heads/sl/my-branch'To git@knot.tngl.boltless.dev:alice.pds.tngl.boltless.dev/core ! [remote rejected] sl/my-branch -> sl/my-branch (failed to update ref)```
Second attempt succeeds, so it looks like a race in the hook rather than apermissions problem.MARKDOWN)" \ --arg createdAt '2025-09-22T10:16:35Z' \ '{repo:$repo, title:$title, body:$body, createdAt:$createdAt}')" >/dev/null
put_record "$STATE_1" "$(jq -nc \ --arg issue "$ISSUE_1" \ --arg state "sh.tangled.repo.issue.state.closed" \ --arg createdAt '2025-09-22T10:17:35Z' \ '{issue:$issue, state:$state, createdAt:$createdAt}')" >/dev/null
# --- pull request fixtures ---
PULL_BASE=01117fddd3502c57bc20a65a75a66a4900d5d67dPULL_HEAD_1=f3c356fc8da46818a8ab0c24f22ee61854fe4b5fPULL_HEAD_2=9a925efef6a0e6dfcd2d4317b4a1eee8752928b8
patch_blob() { curl -fsS "${KNOT_URL}/xrpc/sh.tangled.repo.compare?repo=${REPO_DID}&rev1=${PULL_BASE}&rev2=$1" \ | jq -j '.patch' \ | gzip \ | curl -fsS --data-binary @- \ -H "Content-Type: application/gzip" \ -H "Authorization: Bearer ${OWNER_JWT}" \ "${PDS_URL}/xrpc/com.atproto.repo.uploadBlob" \ | jq -ec '.blob'}
put_record "$PULL_1" "$(jq -nc \ --arg repo "$REPO_DID" \ --arg title 'legacy PR' \ --arg body 'two rounds, each a gzipped format-patch blob' \ --argjson round1 "$(patch_blob "$PULL_HEAD_1")" \ --argjson round2 "$(patch_blob "$PULL_HEAD_2")" \ --arg createdAt1 '2025-09-22T10:40:35Z' \ --arg createdAt2 '2025-09-22T10:41:35Z' \ '{title:$title, body:$body, createdAt:$createdAt1, source:{repo:$repo, branch:"sl/ref-based-pr"}, target:{repo:$repo, branch:"master"}, rounds:[{patchBlob:$round1, createdAt:$createdAt1}, {patchBlob:$round2, createdAt:$createdAt2}]}')" >/dev/null
keep_commit() { did=${3#at://} did=${did%%/*}
curl -fsS \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(service_token "$(login "$did")" sh.tangled.git.keepCommit)" \ -d "$(jq -nc --arg repo "$1" --arg oid "$2" --arg record "$3" \ '{repo:$repo, record:$record, source:{"$type":"sh.tangled.git.keepCommit#commit", repo:$repo, oid:$oid}}')" \ "${KNOT_URL}/xrpc/sh.tangled.git.keepCommit" >/dev/null \ && printf '[keep] %s %s\n' "$2" "$3" >&2 \ || printf '[keep] %s failed, knot has no keepCommit\n' "$2" >&2}
keep_commit "$REPO_DID" "$PULL_HEAD_1" "$PULL_2"keep_commit "$REPO_DID" "$PULL_HEAD_2" "$PULL_2"
put_record "$PULL_2" "$(jq -nc \ --arg repo "$REPO_DID" \ --arg title 'ref based PR' \ --arg body 'something something markdown' \ --arg head1 "$PULL_HEAD_1" \ --arg head2 "$PULL_HEAD_2" \ --arg base "$PULL_BASE" \ --arg createdAt1 '2025-09-22T10:42:35Z' \ --arg createdAt2 '2025-09-22T10:43:35Z' \ '{title:$title, body:$body, createdAt:$createdAt1, source:{repo:$repo, branch:"sl/ref-based-pr"}, target:{repo:$repo, branch:"master"}, rounds:[], versions:[{head:$head1, base:$base, createdAt:$createdAt1}, {head:$head2, base:$base, createdAt:$createdAt2}]}')" >/dev/null
# --- string fixtures ---
STRING_CODE_CID=$(put_record "$STRING_CODE" "$(jq -nc \ --arg filename 'sieve.go' \ --arg description 'primes below n, no allocations past the sieve' \ --arg contents "$(cat <<'CODE'package main
import "fmt"
func sieve(n int) []int { composite := make([]bool, n+1) var primes []int for i := 2; i <= n; i++ { if composite[i] { continue } primes = append(primes, i) for j := i * i; j <= n; j += i { composite[j] = true } } return primes}
func main() { fmt.Println(sieve(50))}CODE)" \ --arg createdAt '2025-09-22T10:18:35Z' \ '{filename:$filename, description:$description, contents:$contents, createdAt:$createdAt}')")
put_record "$STRING_MARKDOWN" "$(jq -nc \ --arg filename 'notes.md' \ --arg description 'scratch notes from bringing up the local stack' \ --arg contents "$(cat <<'MARKDOWN'# Local stack notes
Things that bit me, in order:
1. the dev CA has to be in the **system** trust store, not just the browser2. `TANGLED_APPVIEW_HOST` must be a loopback IP, not `localhost`3. the knot wants `/shared/owner-did` before it will start
## Handy
```shdocker compose logs -f appview | grep ingest```
Still unclear: how [spindle](https://tangled.org/tangled.org/core) picks up`.tangled/workflows`.MARKDOWN)" \ --arg createdAt '2025-09-22T10:19:35Z' \ '{filename:$filename, description:$description, contents:$contents, createdAt:$createdAt}')" >/dev/null
put_record "$STRING_TEXT" "$(jq -nc \ --arg filename 'todo.txt' \ --arg description '' \ --arg contents "$(cat <<'TEXT'- [ ] figure out why the first ssh push races- [x] get the local knot talking to the mirror- [ ] write up the bootstrap order somewhere findable- [ ] ask about symlinks in the tree viewTEXT)" \ --arg createdAt '2025-09-22T10:20:35Z' \ '{filename:$filename, description:$description, contents:$contents, createdAt:$createdAt}')" >/dev/null
# --- comment fixtures ---
COMMENT_ISSUE_CID=$(put_record "$COMMENT_ISSUE" "$(jq -nc \ --arg subjectUri "$ISSUE_2" \ --arg subjectCid "$ISSUE_2_CID" \ --arg text "$(cat <<'MARKDOWN'Worth calling out the ordering explicitly — I lost an afternoon to the knotexiting because `/shared/owner-did` wasn't there yet.MARKDOWN)" \ --arg createdAt '2025-09-22T10:21:35Z' \ '{subject:{uri:$subjectUri, cid:$subjectCid}, createdAt:$createdAt, body:{"$type":"sh.tangled.markup.markdown", text:$text, original:$text}}')")
put_record "$COMMENT_ISSUE_REPLY" "$(jq -nc \ --arg subjectUri "$ISSUE_2" \ --arg subjectCid "$ISSUE_2_CID" \ --arg replyUri "$COMMENT_ISSUE" \ --arg replyCid "$COMMENT_ISSUE_CID" \ --arg text 'Agreed. The compose `depends_on` encodes it already, just nowhere a human would look.' \ --arg createdAt '2025-09-22T10:22:35Z' \ '{subject:{uri:$subjectUri, cid:$subjectCid}, replyTo:{uri:$replyUri, cid:$replyCid}, createdAt:$createdAt, body:{"$type":"sh.tangled.markup.markdown", text:$text, original:$text}}')" >/dev/null
COMMENT_STRING_CID=$(put_record "$COMMENT_STRING" "$(jq -nc \ --arg subjectUri "$STRING_CODE" \ --arg subjectCid "$STRING_CODE_CID" \ --arg text 'Starting the inner loop at `i*i` is the bit everyone forgets.' \ --arg createdAt '2025-09-22T10:23:35Z' \ '{subject:{uri:$subjectUri, cid:$subjectCid}, createdAt:$createdAt, body:{"$type":"sh.tangled.markup.markdown", text:$text, original:$text}}')")
put_record "$COMMENT_STRING_REPLY" "$(jq -nc \ --arg subjectUri "$STRING_CODE" \ --arg subjectCid "$STRING_CODE_CID" \ --arg replyUri "$COMMENT_STRING" \ --arg replyCid "$COMMENT_STRING_CID" \ --arg text 'Only correct because the outer loop skips composites — otherwise it would miss factors.' \ --arg createdAt '2025-09-22T10:24:35Z' \ '{subject:{uri:$subjectUri, cid:$subjectCid}, replyTo:{uri:$replyUri, cid:$replyCid}, createdAt:$createdAt, body:{"$type":"sh.tangled.markup.markdown", text:$text, original:$text}}')" >/dev/null
# --- reaction fixtures ---
put_record "$REACT_ISSUE_A" "$(jq -nc \ --arg subject "$ISSUE_2" \ --arg reaction '🎉' \ --arg createdAt '2025-09-22T10:25:35Z' \ '{subject:$subject, reaction:$reaction, createdAt:$createdAt}')" >/dev/null
put_record "$REACT_ISSUE_B" "$(jq -nc \ --arg subject "$ISSUE_2" \ --arg reaction '👀' \ --arg createdAt '2025-09-22T10:26:35Z' \ '{subject:$subject, reaction:$reaction, createdAt:$createdAt}')" >/dev/null
put_record "$REACT_COMMENT_A" "$(jq -nc \ --arg subject "$COMMENT_ISSUE" \ --arg reaction '👍' \ --arg createdAt '2025-09-22T10:27:35Z' \ '{subject:$subject, reaction:$reaction, createdAt:$createdAt}')" >/dev/null
put_record "$REACT_COMMENT_B" "$(jq -nc \ --arg subject "$COMMENT_STRING" \ --arg reaction '🚀' \ --arg createdAt '2025-09-22T10:28:35Z' \ '{subject:$subject, reaction:$reaction, createdAt:$createdAt}')" >/dev/null
# --- follow fixtures ---
put_record "$FOLLOW_ALICE_BOB" "$(jq -nc \ --arg subject "$BOB_DID" \ --arg createdAt '2025-09-22T10:29:35Z' \ '{subject:$subject, createdAt:$createdAt}')" >/dev/null
put_record "$FOLLOW_ALICE_CHARLIE" "$(jq -nc \ --arg subject "$CHARLIE_DID" \ --arg createdAt '2025-09-22T10:30:35Z' \ '{subject:$subject, createdAt:$createdAt}')" >/dev/null
put_record "$FOLLOW_BOB_ALICE" "$(jq -nc \ --arg subject "$OWNER_DID" \ --arg createdAt '2025-09-22T10:31:35Z' \ '{subject:$subject, createdAt:$createdAt}')" >/dev/null
put_record "$FOLLOW_CHARLIE_ALICE" "$(jq -nc \ --arg subject "$OWNER_DID" \ --arg createdAt '2025-09-22T10:32:35Z' \ '{subject:$subject, createdAt:$createdAt}')" >/dev/null
put_record "$FOLLOW_DAVID_ALICE" "$(jq -nc \ --arg subject "$OWNER_DID" \ --arg createdAt '2025-09-22T10:33:35Z' \ '{subject:$subject, createdAt:$createdAt}')" >/dev/null
put_record "$FOLLOW_DAVID_BOB" "$(jq -nc \ --arg subject "$BOB_DID" \ --arg createdAt '2025-09-22T10:34:35Z' \ '{subject:$subject, createdAt:$createdAt}')" >/dev/null
# --- vouch fixtures (subject is the rkey, not a record field) ---
put_record "$VOUCH_ALICE_BOB" "$(jq -nc \ --arg reason 'Reviewed his bootstrap-ordering work on core, knows the stack.' \ --arg evidence "$COMMENT_ISSUE" \ --arg createdAt '2025-09-22T10:35:35Z' \ '{kind:"vouch", reason:$reason, evidences:[$evidence], createdAt:$createdAt}')" >/dev/null
put_record "$VOUCH_BOB_CHARLIE" "$(jq -nc \ --arg reason 'Worked together on the knot ACL rewrite.' \ --arg createdAt '2025-09-22T10:36:35Z' \ '{kind:"vouch", reason:$reason, createdAt:$createdAt}')" >/dev/null
put_record "$VOUCH_CHARLIE_DAVID" "$(jq -nc \ --arg reason 'Long-time collaborator, reviews carefully.' \ --arg createdAt '2025-09-22T10:37:35Z' \ '{kind:"vouch", reason:$reason, createdAt:$createdAt}')" >/dev/null
put_record "$VOUCH_DAVID_ALICE" "$(jq -nc \ --arg reason 'Runs the knot, has never lost a repo.' \ --arg createdAt '2025-09-22T10:38:35Z' \ '{kind:"vouch", reason:$reason, createdAt:$createdAt}')" >/dev/null
put_record "$VOUCH_BOB_DAVID" "$(jq -nc \ --arg reason 'Force-pushed over master twice in one week.' \ --arg createdAt '2025-09-22T10:39:35Z' \ '{kind:"denounce", reason:$reason, createdAt:$createdAt}')" >/dev/null
printf 'done.\n' >&2