# SourceHut (sr.ht) Workflow Reference A complete operational guide for sr.ht: repositories, builds, mailing lists, issue tracking, pages, and the GraphQL API. --- ## 1. hut CLI Setup ### Install ```bash # Arch Linux paru -S hut # From source git clone https://git.sr.ht/~emersion/hut cd hut && go build . && mv hut ~/.local/bin/ ``` ### Initialize ```bash hut init ``` This creates `~/.config/hut/config` interactively. Alternatively, write it manually. ### ~/.config/hut/config ```scfg access-token-cmd cat ~/.config/srht/token instance "git.sr.ht" { access-token-cmd cat ~/.config/srht/token } instance "builds.sr.ht" { access-token-cmd cat ~/.config/srht/token } instance "todo.sr.ht" { access-token-cmd cat ~/.config/srht/token } instance "lists.sr.ht" { access-token-cmd cat ~/.config/srht/token } instance "meta.sr.ht" { access-token-cmd cat ~/.config/srht/token } instance "pages.sr.ht" { access-token-cmd cat ~/.config/srht/token } ``` The `access-token-cmd` can be any shell command that prints the token to stdout. Common patterns: ```scfg # From a file access-token-cmd cat ~/.config/srht/token # From pass access-token-cmd pass show sourcehut/token # From a secret manager access-token-cmd secret-tool lookup service sourcehut key token ``` ### Per-repo .hut.scfg Place `.hut.scfg` in a repo root to override the global config for that repository: ```scfg # .hut.scfg git-repo "~username/repo-name" build-instance "builds.sr.ht" lists-repo "~username/my-list" ``` --- ## 2. Repository Management ### Create a Repository ```bash hut git create my-repo hut git create my-repo --description "A short description" hut git create my-repo --visibility private # public | private | unlisted ``` ### List Repositories ```bash hut git list hut git list --user ~otheruser ``` ### Update Repository Metadata ```bash hut git update my-repo --description "New description" hut git update my-repo --visibility unlisted hut git update my-repo --readme README.md # set rendered README ``` ### Delete a Repository ```bash hut git delete my-repo ``` ### Clone ```bash git clone https://git.sr.ht/~username/repo-name git clone git@git.sr.ht:~username/repo-name # SSH ``` After creating a repo on sr.ht: ```bash git remote add origin git@git.sr.ht:~username/repo-name git push -u origin main ``` ### Access Control (ACL) ```bash # Grant read access to another user hut git acl grant my-repo ~collaborator read # Grant write access hut git acl grant my-repo ~collaborator write # Revoke access hut git acl revoke my-repo ~collaborator # List ACL hut git acl list my-repo ``` ### Artifact Upload Attach binary artifacts to a tag or commit: ```bash # Upload artifact to a release tag hut git artifact upload my-repo v1.0.0 dist/my-binary.tar.gz # Upload multiple artifacts hut git artifact upload my-repo v1.0.0 dist/*.tar.gz # List artifacts hut git artifact list my-repo v1.0.0 # Delete artifact hut git artifact delete my-repo v1.0.0 my-binary.tar.gz ``` --- ## 3. Build System ### Submit a Build ```bash # Submit the .build.yml in the current directory hut builds submit .build.yml # Submit with a specific note hut builds submit .build.yml --note "Testing auth fix" # Submit targeting a specific tag hut builds submit .build.yml --tags "my-tag,another-tag" ``` ### List Builds ```bash hut builds list hut builds list --count 20 hut builds list --status failed ``` ### Show Build Details ```bash hut builds show 12345 ``` ### Follow Build Output (Live) ```bash hut builds show 12345 -f ``` The `-f` flag tails the log output in real time, similar to `tail -f`. ### Cancel a Build ```bash hut builds cancel 12345 ``` ### Resubmit a Build ```bash hut builds resubmit 12345 ``` ### SSH into a Failed Build After a build fails, it stays alive for a short window so you can SSH in and debug: ```bash hut builds ssh 12345 ``` This connects you to the build runner VM at the point of failure. Useful for interactive debugging of flaky build environments. --- ## 4. .build.yml Manifest Reference Full key reference: ```yaml # Image: distribution to run the build on # Format: / image: archlinux # Architecture: x86_64 (default), aarch64 arch: x86_64 # Packages to install before running tasks packages: - git - go - make # Repositories/sources to clone into the build environment # Format: or # sources: - https://git.sr.ht/~username/repo-name - https://git.sr.ht/~username/other-repo#feature-branch # Artifacts: files to extract from the build and attach to the job artifacts: - repo-name/dist/binary - repo-name/dist/archive.tar.gz # Environment variables (plain values only; use secrets for sensitive data) environment: GO111MODULE: "on" GOPROXY: "https://proxy.golang.org" BUILD_VERSION: "1.0.0" # Secrets: UUIDs from builds.sr.ht/secrets # Placed at ~/.secrets/ inside the build VM secrets: - 00000000-0000-0000-0000-000000000000 # OAuth: grant the build job an API token with these grants # Allows the build to interact with sr.ht services oauth: - pages.sr.ht/PAGES:RW # Shell: drop into a shell after the last task (for debugging) # Only useful when submitting manually; auto-triggered builds ignore this shell: false # Triggers: actions to run after the build completes triggers: - action: email condition: failure to: you@example.com - action: webhook condition: always url: https://example.com/hook - action: email condition: always cc: team@example.com # Tasks: ordered list of named shell scripts # Each task runs in a fresh shell; environment is shared tasks: - setup: | cd repo-name go mod download - test: | cd repo-name go test ./... - build: | cd repo-name make release - upload: | cd repo-name hut pages publish -d my-site dist/ ``` ### Available Images Common images (check builds.sr.ht for the full list): | Image | Notes | |-------|-------| | `archlinux` | Rolling release | | `alpine/edge` | Minimal, fast | | `alpine/3.19` | Pinned Alpine | | `debian/bookworm` | Debian stable | | `ubuntu/jammy` | Ubuntu 22.04 | | `fedora/39` | Fedora | | `freebsd/14.0` | FreeBSD | | `openbsd/7.4` | OpenBSD | | `netbsd/10.0` | NetBSD | --- ## 5. Auto-trigger Patterns ### Trigger on Push Place `.build.yml` at the repository root. Every push to any ref triggers a build automatically. No webhook configuration required. ``` repo/ .build.yml <- triggers on every push src/ ``` ### Multiple Build Manifests Place multiple manifests in `.builds/`: ``` repo/ .builds/ linux-amd64.yml linux-arm64.yml test.yml src/ ``` All files in `.builds/` are submitted on every push. The `.build.yml` at the root is also submitted if present. ### Skip CI Append `[skip ci]` or `[ci skip]` to the commit message subject: ```bash git commit -m "Fix typo in README [skip ci]" ``` ### Custom Build on Push Push with options to select a specific manifest: ```bash # Submit a specific manifest file on push git push -o submit=.builds/release.yml # Submit multiple manifests git push -o submit=.builds/test.yml -o submit=.builds/release.yml # Skip all CI git push -o skip-ci ``` ### Branch-Specific Triggers Use the `triggers` key with conditions inside the manifest, or name manifests descriptively and use push options to control which runs. --- ## 6. Secret Management ### Create a Secret on builds.sr.ht Visit https://builds.sr.ht/secrets and create a secret file, environment variable, or SSH key. You get a UUID. ### Reference in .build.yml ```yaml secrets: - a1b2c3d4-e5f6-7890-abcd-ef1234567890 ``` Secret files land at `~/.secrets/` inside the build VM. ### Use Secrets Safely Always disable command tracing before reading a secret: ```yaml tasks: - deploy: | set +x # disable xtrace source ~/.secrets/a1b2c3d4-e5f6-7890-abcd-ef1234567890 set -x # re-enable xtrace deploy --token "$SECRET_TOKEN" ``` The `set +x` pattern prevents the secret value from appearing in the build log. Without it, every executed command including variable assignments prints to the log. ### Environment Variable Secrets When the secret is an environment variable file (key=value format), source it: ```yaml tasks: - auth: | set +x source ~/.secrets/ set -x curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com ``` --- ## 7. Patch Workflow ### Configure git send-email with msmtp Install msmtp and configure it at `~/.config/msmtp/config`: ``` defaults tls on tls_trust_file /etc/ssl/certs/ca-certificates.crt logfile ~/.config/msmtp/msmtp.log account sourcehut host mail.sourcehut.org port 587 auth on user your@email.com passwordeval cat ~/.config/msmtp/password from your@email.com account default: sourcehut ``` Then configure git to use msmtp: ```bash git config --global sendemail.smtpserver /usr/bin/msmtp git config --global sendemail.smtpserveroption "-a sourcehut" git config --global sendemail.confirm always git config --global sendemail.suppresscc self git config --global format.signOff true ``` ### Per-repo sendemail.to Configure the target mailing list per repository: ```bash cd my-repo git config sendemail.to "~username/list-name@lists.sr.ht" ``` Now `git send-email` targets the correct list by default for that repo. ### Send a Single Patch ```bash # Create a patch from the last commit git format-patch -1 HEAD # Send it git send-email 0001-my-fix.patch ``` ### Send Multiple Patches as a Series ```bash # Create patches for the last 3 commits git format-patch -3 HEAD # Send with a cover letter git send-email --cover-letter --annotate 0001-*.patch 0002-*.patch 0003-*.patch ``` The `--annotate` flag opens each patch in your editor before sending. ### Send a Revision of a Previous Series ```bash # Mark as revision 2 git send-email -v2 --cover-letter 0001-*.patch ``` This prepends `[PATCH v2]` to the subject, linking reviewers to the previous series. ### In-Reply-To (Threading) To thread a revision under the original thread: ```bash # Get the Message-ID of the original cover letter from the list archive git send-email -v2 --in-reply-to="" 0001-*.patch ``` --- ## 8. Mailing Lists ### Create a List ```bash hut lists create my-list --description "Patches and discussion" ``` ### Subscribe to a List ```bash hut lists subscribe ~username/my-list your@email.com ``` ### Archive a List ```bash hut lists archive ~username/my-list ``` ### List Patchsets ```bash hut lists patchset list ~username/my-list ``` ### Show a Patchset ```bash hut lists patchset show ~username/my-list 42 ``` ### Update Patchset Status ```bash hut lists patchset update-status ~username/my-list 42 APPLIED hut lists patchset update-status ~username/my-list 42 NEEDS_REVISION hut lists patchset update-status ~username/my-list 42 REJECTED ``` Valid statuses: `PROPOSED`, `NEEDS_REVISION`, `SUPERSEDED`, `APPROVED`, `REJECTED`, `APPLIED` --- ## 9. Issue Tracking (todo.sr.ht) ### Create a Tracker ```bash hut todo create my-tracker --description "Bug reports and features" ``` ### Create a Ticket ```bash hut todo ticket create ~username/my-tracker --title "Something is broken" # With a body from stdin echo "Details about the bug." | hut todo ticket create ~username/my-tracker --title "Bug report" # With body from a file hut todo ticket create ~username/my-tracker --title "Feature request" --body feature.md ``` ### Comment on a Ticket ```bash hut todo ticket comment ~username/my-tracker 7 --comment "I can reproduce this." ``` ### Update Ticket Status ```bash hut todo ticket update-status ~username/my-tracker 7 RESOLVED hut todo ticket update-status ~username/my-tracker 7 CLOSED hut todo ticket update-status ~username/my-tracker 7 IN_PROGRESS ``` Valid statuses: `REPORTED`, `CONFIRMED`, `IN_PROGRESS`, `PENDING`, `RESOLVED`, `CLOSED` ### Add a Label ```bash hut todo ticket label ~username/my-tracker 7 bug hut todo ticket label ~username/my-tracker 7 enhancement ``` ### Assign a Ticket ```bash hut todo ticket assign ~username/my-tracker 7 ~collaborator ``` --- ## 10. Pages Deployment ### Publish a Directory ```bash # Publish the current directory hut pages publish # Publish a specific directory hut pages publish -d my-domain.tld dist/ # Publish and set the domain hut pages publish -d my-domain.tld --domain my-domain.tld dist/ ``` ### In-build Deployment Use the `oauth` key to grant the build job pages write access, then call `hut pages publish` from within a task: ```yaml image: archlinux packages: - hut - nodejs - npm oauth: - pages.sr.ht/PAGES:RW sources: - https://git.sr.ht/~username/my-site tasks: - build: | cd my-site npm ci npm run build - deploy: | cd my-site hut pages publish -d my-domain.tld out/ ``` The `oauth` grant creates a short-lived token inside the build environment that `hut` picks up automatically. --- ## 11. Paste ### Create a Paste from a File ```bash hut paste create myfile.txt # With a custom filename shown in the UI hut paste create myfile.txt --filename "config.fish" # Set visibility hut paste create myfile.txt --visibility unlisted ``` ### Create a Paste from stdin ```bash # Pipe directly cat somefile.txt | hut paste create # Here string hut paste create <<'EOF' This is my paste content. EOF # Command output git diff HEAD | hut paste create --filename "my.diff" ``` --- ## 12. GraphQL API All sr.ht services expose a GraphQL API. The `hut graphql` command sends raw queries. ### Send a Query ```bash # Query against a specific service hut graphql git.sr.ht < query.graphql # Inline query echo '{ me { username email } }' | hut graphql meta.sr.ht # With variables (JSON) hut graphql git.sr.ht --variables '{"name": "my-repo"}' < query.graphql ``` ### curl Patterns All services accept `Authorization: Bearer ` with a JSON body containing `query` and optionally `variables`: ```bash TOKEN=$(cat ~/.config/srht/token) # meta.sr.ht: get current user curl -s https://meta.sr.ht/query \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "{ me { username email } }"}' | jq . # git.sr.ht: list repos curl -s https://git.sr.ht/query \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "{ repositories { results { name visibility } } }"}' | jq . # builds.sr.ht: get a build curl -s https://builds.sr.ht/query \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "{ job(id: 12345) { status log } }"}' | jq . ``` --- ## 13. Webhook Configuration ### Create a Git Webhook ```bash hut git webhook create my-repo \ --url https://example.com/hook \ --events repo:post-update ``` ### List Webhooks ```bash hut git webhook list my-repo ``` ### Delete a Webhook ```bash hut git webhook delete my-repo ``` ### Verify Webhook Signatures sr.ht signs webhook payloads with Ed25519. The public key is published at `https://meta.sr.ht/.well-known/sourcehut/ed25519`. Example verification in Python: ```python from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey from cryptography.hazmat.primitives.serialization import load_pem_public_key import base64, hashlib def verify_webhook(public_key_pem: bytes, payload: bytes, signature_b64: str) -> bool: pub = load_pem_public_key(public_key_pem) sig = base64.b64decode(signature_b64) digest = hashlib.sha256(payload).digest() try: pub.verify(sig, digest) return True except Exception: return False ``` The `X-Sourcehut-Signature` header contains the base64-encoded signature. ### GraphQL Query in a Webhook Handler Webhooks send JSON payloads. To respond by querying the API: ```bash #!/usr/bin/env bash # Called by webhook, reads JSON from stdin PAYLOAD=$(cat) REPO=$(echo "$PAYLOAD" | jq -r '.repo.name') TOKEN=$(cat ~/.config/srht/token) curl -s https://git.sr.ht/query \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{\"query\": \"{ repository(name: \\\"$REPO\\\") { HEAD { shorthand } } }\"}" | jq . ``` --- ## 14. aerc Email Client aerc integrates cleanly with mailing lists and patch workflows on sr.ht. ### accounts.conf ```ini [sourcehut] source = imaps://your@email.com@imap.fastmail.com:993 outgoing = msmtp -a sourcehut default = INBOX smtp-starttls = yes from = Your Name copy-to = Sent ``` ### Keybindings for Patch Workflows In `~/.config/aerc/binds.conf`: ```ini [messages] # Apply a patch from a message gp = :pipe -m git am -3 # Reply with patch review gr = :reply # Mark patchset as applied (send X-Sourcehut-Patchset-Update) ga = :reply -a:header X-Sourcehut-Patchset-Update APPLIED gn = :reply -a:header X-Sourcehut-Patchset-Update NEEDS_REVISION [compose] # Toggle quoted reply = :toggle-headers ``` ### Reply Templates with X-Sourcehut-Patchset-Update Create `~/.config/aerc/templates/patch-applied`: ``` X-Sourcehut-Patchset-Update: APPLIED Thanks, applied to main. ``` Use in aerc: ``` :reply -T patch-applied ``` When lists.sr.ht sees `X-Sourcehut-Patchset-Update` in a reply to a patchset thread, it updates the patchset status automatically. --- ## 15. b4 Integration with sr.ht Lists b4 is a tool for working with patches sent to mailing lists. It can pull patchsets from lists.sr.ht archives. ### Install ```bash paru -S b4 ``` ### Fetch a Patchset by Message-ID ```bash b4 am ``` This downloads all patches in the series and prepares an `mbox` file ready for `git am`. ### Apply the Patchset ```bash b4 am -o patches/ git am patches/*.mbox ``` ### Configure b4 for sr.ht In `~/.config/b4/config`: ```ini [b4] midmask = https://lists.sr.ht/%s linkmask = https://lists.sr.ht/%s ``` ### Fetch Latest Revision ```bash # Gets the latest revision of a patch thread b4 am --use-version 2 ``` --- ## 16. X-Sourcehut-Patchset-Update Header When you reply to a patchset thread on a lists.sr.ht mailing list and include this header, lists.sr.ht automatically updates the patchset status. | Value | Meaning | |-------|---------| | `PROPOSED` | Patch submitted for review (default state) | | `NEEDS_REVISION` | Reviewer requests changes | | `SUPERSEDED` | This version replaced by a new revision | | `APPROVED` | Reviewer approves, ready to merge | | `REJECTED` | Patch will not be merged | | `APPLIED` | Maintainer has applied the patch | ### Usage in a Reply When composing a review reply in any mail client: ``` X-Sourcehut-Patchset-Update: NEEDS_REVISION The approach looks good but the error handling needs work. See inline comments. ``` Only maintainers and the original submitter can change status. lists.sr.ht validates permissions before updating. --- ## 17. Email Etiquette sr.ht's developer community follows strict plain-text email conventions. Violating these makes patches harder to review and apply. ### Rules **Plain text only.** No HTML. No rich text. Set your mail client to always send plain text. **No top-posting.** Quote the relevant context, then reply below it. When reviewing a patch, quote the relevant diff hunk and comment inline beneath it. **72-column line wrap.** Wrap prose at 72 characters. Patch content (diff lines) can be longer, but your commentary should be wrapped. Most email clients and `git format-patch` handle this automatically. **Trim quoted context.** Quote only what you are directly responding to. Do not quote entire threads. **Inline review comments.** For patches, quote the specific hunk you are commenting on and place your comment directly below it. Use `>` for quoted lines. **Subject lines.** Keep them clear and concise. For patch revisions, include `[PATCH v2]` etc. **Signature.** Keep it short (four lines maximum, prefixed with `-- `). ### aerc Plain Text Config In `~/.config/aerc/aerc.conf`: ```ini [compose] editor = nvim ``` aerc defaults to plain text composition. Avoid HTML or rich-text plugins. --- ## 18. GraphQL Endpoints Table | Service | Endpoint | |---------|----------| | meta.sr.ht | `https://meta.sr.ht/query` | | git.sr.ht | `https://git.sr.ht/query` | | builds.sr.ht | `https://builds.sr.ht/query` | | todo.sr.ht | `https://todo.sr.ht/query` | | lists.sr.ht | `https://lists.sr.ht/query` | | pages.sr.ht | `https://pages.sr.ht/query` | | paste.sr.ht | `https://paste.sr.ht/query` | All endpoints accept `POST` with `Content-Type: application/json` and `Authorization: Bearer `. Interactive GraphQL explorer (GraphiQL) is available by visiting each endpoint in a browser with a valid token. --- ## 19. Useful GraphQL Queries ### List Repositories ```graphql { repositories { results { name description visibility updated } } } ``` ### Create a Repository ```graphql mutation { createRepository(name: "new-repo", visibility: PUBLIC, description: "My repo") { id name } } ``` ### Check Build Status ```graphql { job(id: 12345) { status note tags tasks { name status } } } ``` ### List Recent Builds ```graphql { jobs(count: 10) { results { id status note created } } } ``` ### Submit a Ticket ```graphql mutation { submitTicket( tracker: "~username/my-tracker" input: { subject: "Bug: something is wrong" body: "Steps to reproduce..." } ) { id ref } } ``` ### List SSH Keys ```graphql { me { sshKeys { id fingerprint comment created } } } ``` ### Add an SSH Key ```graphql mutation { createSSHKey(key: "ssh-ed25519 AAAA... comment") { id fingerprint } } ``` ### Get Current User Info ```graphql { me { username email url created location bio } } ``` --- ## 20. Fish Shell Functions for sr.ht Operations Add these to `~/.config/fish/config.fish` or a file in `~/.config/fish/functions/`: ```fish # sr.ht token shortcut function srht_token cat ~/.config/srht/token end # GraphQL query helper function srht_gql set -l service $argv[1] set -l query $argv[2] set -l token (srht_token) curl -s "https://$service/query" \ -H "Authorization: Bearer $token" \ -H "Content-Type: application/json" \ -d (echo "{\"query\": $query}" | jq -c .) | jq . end # Quick repo create + remote setup function srht_new set -l name $argv[1] set -l desc $argv[2] hut git create $name --description $desc git remote add origin "git@git.sr.ht:~"(hut graphql meta.sr.ht -e '{ me { username } }' | jq -r .data.me.username)"/$name" echo "Remote added. Push with: git push -u origin main" end # Clone a sr.ht repo by shorthand (~user/repo) function srht_clone set -l parts (string split / $argv[1]) set -l user $parts[1] # e.g. ~username set -l repo $parts[2] git clone "https://git.sr.ht/$user/$repo" end # Submit a build and follow the log function srht_build set -l manifest $argv[1] if test -z "$manifest" set manifest .build.yml end set -l id (hut builds submit $manifest | grep -oP 'job #\K[0-9]+') echo "Build #$id submitted" hut builds show $id -f end # Watch latest build function srht_watch set -l id (hut builds list --count 1 | awk 'NR==2 {print $1}') hut builds show $id -f end # Send a patch series to the configured list function srht_send set -l count $argv[1] if test -z "$count" set count 1 end git format-patch -$count HEAD git send-email --annotate (ls -t *.patch | head -$count | sort) end # Paste a file and print the URL function srht_paste hut paste create $argv[1] | grep -oP 'https://paste\.sr\.ht/\S+' end # Paste stdin function srht_pastein cat | hut paste create | grep -oP 'https://paste\.sr\.ht/\S+' end # Open a repo in the browser function srht_open set -l name $argv[1] set -l user (hut graphql meta.sr.ht -e '{ me { username } }' 2>/dev/null | jq -r .data.me.username 2>/dev/null) if test -z "$user" set user (cat ~/.config/srht/username 2>/dev/null) end xdg-open "https://git.sr.ht/~$user/$name" end # List all repos with visibility function srht_repos echo '{ repositories { results { name visibility description } } }' \ | hut graphql git.sr.ht \ | jq -r '.data.repositories.results[] | "\(.visibility)\t\(.name)\t\(.description // "")"' \ | column -t -s (printf '\t') end # Check build status for a repo's latest push function srht_status hut builds list --count 5 | column -t end # Update patchset status from the command line function srht_patch_status set -l tracker $argv[1] set -l id $argv[2] set -l status $argv[3] hut lists patchset update-status $tracker $id $status end ``` ### Usage Examples ```fish # Create a new repo srht_new my-project "A cool project" # Clone someone's repo srht_clone ~sircmpwn/aerc # Submit build and watch srht_build .build.yml # Send last 3 commits as a patch series srht_send 3 # Paste a diff git diff | srht_pastein # List all your repos srht_repos # Mark patchset 42 as applied srht_patch_status ~username/my-list 42 APPLIED ``` --- ## Quick Reference Card ``` hut git create create repo hut git list list repos hut git update --description X update metadata hut git acl grant ~user write share access hut git artifact upload attach release artifact hut builds submit .build.yml submit build hut builds show -f follow build log hut builds ssh debug failed build hut todo ticket create ~u/t --title X new ticket hut todo ticket update-status ~u/t N S change ticket status hut lists patchset list ~u/l list patchsets hut lists patchset update-status ~u/l N APPLIED hut pages publish -d domain.tld dist/ publish site hut paste create file.txt create paste echo "..." | hut paste create paste from stdin git push -o skip-ci skip build trigger git push -o submit=.builds/release.yml trigger specific manifest git format-patch -3 HEAD prepare 3 patches git send-email --cover-letter *.patch send patch series ```