# Mirrors main to GitHub and Tangled on every push. Knotless is the source # of truth: nothing pushes the other way, and a mirror only ever # fast-forwards, so a commit made on GitHub or Tangled directly makes the # run fail rather than be overwritten. # # A run pushes main as knotless has it when the run starts, so a run that a # newer push overtakes still leaves the mirrors at the newest commit. Each # mirror is tried even when the other fails, and the run fails if either # did. # # It runs on ci.knotless.dev alone, like deploy, so the keys never reach # the hosted runner that checks pulls. Both are sealed to pushes to main # (`knotless secret knotless://grain.social/grain set --runner # ci.knotless.dev --ref refs/heads/main`): # # GITHUB_DEPLOY_KEY a write deploy key on github.com/grainsocial/grain # TANGLED_SSH_KEY a key on the grain.social account on tangled.org # # `knotless rerun knotless://grain.social/grain --ref refs/heads/main # --workflow mirror` mirrors main again, which is how to repair a mirror # after a failed run. on: push: { refs: [refs/heads/main] } asked: { refs: [refs/heads/main] } image: alpine:3.22 runners: [ci.knotless.dev] secrets: [GITHUB_DEPLOY_KEY, TANGLED_SSH_KEY] env: SOURCE: https://knotless.dev/grain.social/grain.git steps: - name: tools run: apk add --no-cache git openssh-client - name: keys run: | mkdir -p ~/.ssh && chmod 700 ~/.ssh printf '%s\n' "$GITHUB_DEPLOY_KEY" > ~/.ssh/github && chmod 600 ~/.ssh/github printf '%s\n' "$TANGLED_SSH_KEY" > ~/.ssh/tangled && chmod 600 ~/.ssh/tangled printf 'Host github.com\n IdentityFile ~/.ssh/github\n IdentitiesOnly yes\nHost tangled.org\n IdentityFile ~/.ssh/tangled\n IdentitiesOnly yes\n' > ~/.ssh/config # Both hosts' ed25519 keys, pinned. GitHub publishes its own at # api.github.com/meta. cat > ~/.ssh/known_hosts <<'EOF' github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl tangled.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII2UShEm/FFPmYZUizaPnIqOJuynoCQpcLhl5PPHd02n EOF - name: mirror timeout: 10 run: | # A full fetch, not the checkout: a mirror is sent the history between # its head and this commit, and a shallow repository cannot send it. git init -q --bare /mirror && cd /mirror git fetch -q "$SOURCE" "+refs/heads/main:refs/heads/main" echo "main is at $(git rev-parse refs/heads/main)" failed="" for mirror in git@github.com:grainsocial/grain.git git@tangled.org:grain.social/grain; do echo "==> $mirror" git push "$mirror" refs/heads/main:refs/heads/main || failed="$failed $mirror" done if [ -n "$failed" ]; then echo "not mirrored to:$failed" exit 1 fi