entangle #
Language: Rust
Output: CLI tool
Target: Linux, MacOS, Windows
Goal: Easy setup for mirroring GitHub repos to Tangled.org in one command
Manual process this is replacing #
From the Tangled.org docs, the thing I have to do by hand at the moment:
You can configure your local repository to push to both Tangled and, say, GitHub. You may already have the following setup:
$ git remote -v
origin git@github.com:username/my-project.git (fetch)
origin git@github.com:username/my-project.git (push)
Now add Tangled as an additional push URL to the same remote:
git remote set-url --add --push origin git@tangled.org:user.tngl.sh/my-project
You also need to re-add the original URL as a push destination (Git will now use the original URL to fetch only):
git remote set-url --add --push origin git@github.com:username/my-project.git
Verify your configuration:
$ git remote -v
origin git@github.com:username/my-project.git (fetch)
origin git@tangled.org:user.tngl.sh/my-project (push)
origin git@github.com:username/my-project.git (push)
Notice that there’s one fetch URL (the primary remote) and two push URLs.
The goal is to never do that manually again.
Commands to implement #
entangle init: interactive if empty, orentangle init repo-name-as-positional-argument optional-alias-as-positional-argumententangle setup: interactive setup of GitHub and Tangled usernames and default origin preference (defaults to GitHub)entangle set [gh-user | github-user || tngl-user | tangled-user || origin {gh | github || tngl | tangled}]to manually establish config preferencesentangle shovefinishing-up helper: convenience alias forgit push origin --all && git push origin --tags. One-time "push the whole thing" helper for the first sync afterentangle init; sinceoriginalready has two push URLs configured, one command hits both forges automatically.
Using entangle setup #
- Setup creates a JSON config file at
.config/entangle/config.jsonor appropriate equivalent (usedirscrate to handle directories) - Interactive setup: prompt for GitHub username, Tangled username, and origin preference (GitHub or Tangled, default GitHub), in that order
- Individual features set with
entangle set:gh-user | github-user username,tngl-user | tangled-user username,origincan begh | githubortngl | tangled. entangle setmodifies the globalconfig
Config file format: #
{
"github_username": "...",
"tangled_username": "...",
"origin_preference": "github" // or "tangled"
}
origin_preference: which remote is the fetch remote.
Example of entangle set #
# Pretend you're me
entangle set gh-user cyrusae # this checks GitHub username validity
entangle set tngl-user atdot.fyi # this checks ATProto username validity
entangle set origin github # this is already the default option
Using entangle init #
When a user runs entangle init, what should happen:
- Check if a valid config exists; if not, stop and refer the user to
entangle setup - Without arguments: prompt for repo name and then for optional mirror name
- With arguments:
entangle init arg1 arg2wherearg1is the repo name andarg2is the optional alias for its mirror (i.e., by default,arg1is the GitHub repo name andarg2if present becomes the Tangled repo name) - Check whether name(s) given are valid repo names
- Build the prospective GitHub and Tangled URLs
- GitHub:
git@github.com:{github_username}/{repo}.git - Tangled:
git@tangled.org:{tangled_username}/{repo}(note: no.git; Tangled username is a full ATProto handle, e.g.atdot.fyi)
- GitHub:
- Check if intended remotes are valid — two-stage:
- Local regex first: validate the constructed URL strings before touching the network (fast fail on obviously malformed input)
gixSSHls-refs: attempt a real connection to each remote (equivalent togit ls-remote). Fail the default-origin remote first. Distinguish three error cases:- Not found: repo doesn't exist at that URL → stop, warn user to check repo name / whether the repo has been initialized on that forge
- Auth failure: SSH handshake failed → stop, warn user their SSH key may not be configured for that forge (different problem from a typo)
- Network error (timeout, no route to host): → warn and offer an override prompt ("Couldn't reach remote. Accept anyway? [y/N]") so the tool remains usable offline
- Note: private GitHub repos are handled naturally by this approach — if the user's SSH key has access,
ls-refssucceeds regardless of repo visibility. No special-casing needed.
- Check if the folder is already a git repository
- If not,
git initand print informative message ("Folder is not a git repository, initializing...") - Check for
.gitignoreandREADME.md; suggest user to add them on their next commit if either or both are missing
- If not,
- Check if remotes are already configured
- If both GitHub and Tangled remotes exist according to
git remote -v, stop early - If
origindoesn't match the generatedoriginURL based onrepo-name, prompt:An origin remote already exists: git@gitlab.com:someone/something.git Replace it with git@github.com:{user}/{repo}.git? [Y/n]- Yes: replace and continue normally
- No: prompt
Add push URLs to existing origin anyway? [Y/n]- Yes: continue (origin fetch URL stays as-is; push URLs will be added to whatever is there — this is unusual but not blocked)
- No: abort
- If both GitHub and Tangled remotes exist according to
- Add the non-default remote as push origin
- Re-add the default remote as push origin (order matters, default last)
- Finish and return to user verification of the set remotes
Ending prompt should include a suggestion to entangle shove for a first-time mirror to sync all repo contents (including all branches).
Coding and preferences #
- Use the
gixcrate for interacting with git. Avoid shelling out togitexcept for tests. - Modular code: if I have to scroll twice, the file might be doing too many things at once.
- Add documentation comments.
- Comment prolifically in general.
- Unit tests and integration tests: use
gixfor unit tests, directly usegitfor integration tests. See the testing doc for details.
Test cases and sanitization #
See the testing doc for details.
Decisions to make during development #
- Consistent style guide for progress messages
- Consistent style guide for error messages
- Formatting of terminal output
- Crates for terminal output (decided):
clap— argument parsingdialoguer— interactive prompts (setup, Y/n confirmations, "already set to X, change?" pattern); handles Ctrl+C gracefullyindicatif— spinners/progress for network calls (ls-refscan take a moment)owo-colors— terminal colorsserde+serde_json— config file serializationdirs— platform-aware config directory (already noted above)gix— all git operations (already noted above)
- Comprehensive test suite — how to maximize coverage?
Post-MVP considerations/additions #
- Add
-o | --overwriteflag to skip the overwriting warnings- Decision point: How aggressively does
--overwriteletentangleact? Decide this based on failure cases established during testing.
- Decision point: How aggressively does
- Add
-q | --quietflag to do things silently instead of verbosely (I prefer verbose as a default), and/or aconfig-level preference on verbosity (default"verbosity_preference": "verbose")- Decision point: What output still happens if
quietis on? Decide this based on the final draft of the verbose messages.
- Decision point: What output still happens if
entangle helpor-h/--helpshows help;entangle command --helpshows per-command help;entanglelists valid commandsentangle versionor-v/--versionshows versionentangle one-or-more illegal-arguments from-a-userfails helpfully