configrepo
Copies a project's editor directory — .turbo-go, .turbo-rust — out of a repository somebody shares, from the URL a forge shows for the directory holding it.
turbo-go -load-config https://rickub.com/turbo-editors/configs/tree/main/golang-init
fetches golang-init/.turbo-go into the working directory.
Why git, not an API
The URL can be on rickub, GitHub, GitLab or Codeberg. Each has a JSON API for reading a directory, each API is different, and rickub's wants a personal access token even to read a public repository. Git is the one thing all of them speak the same way, with no token: the fetch is git ls-remote to find the repository and its branches, then git clone --depth 1 --branch <ref> into a temporary directory, then a copy. The price is that git must be installed, which on the machine of somebody running a terminal IDE it is. ErrNoGit is what you get otherwise.
Reading the URL
Parse understands what the browsers show:
| Shape | Forge |
|---|---|
https://host/owner/repo/tree/main/dir |
rickub, GitHub |
https://host/group/sub/repo/-/tree/main/dir |
GitLab — the owner keeps its slashes |
https://host/owner/repo/src/branch/main/dir |
Codeberg, Forgejo, Gitea — also src/tag/, src/commit/ |
https://host/owner/repo |
a repository: default branch, root |
blob/ is taken like tree/, so a URL to a file inside the directory works too.
A branch with a slash in it — feature/x — cannot be told from the path by the URL alone. Parse takes the first segment as the ref and the rest as the path; Load then asks the repository for its branches and tags (git ls-remote) and resolveRef picks the longest one that begins ref/path, giving the rest back to the path. Nothing matching leaves both as they were and the clone reports the missing branch in git's own words.
Where to clone from. GitHub, GitLab and Codeberg serve git at the address the browser shows. rickub does not — pages on rickub.com, repositories on git.rickub.com — so CloneURLs offers https://host/owner/repo.git and then https://git.host/owner/repo.git, and firstAnswering takes the first whose ls-remote answers. The listing does two jobs at once: telling the candidates apart, and being what a slashed ref is resolved against.
What is copied, and where
Inside the checkout, locate looks for <path>/.turbo-go, or takes <path> itself when its last segment already is .turbo-go. Every regular file under it is copied with projectfile.Write — same mode, same atomic rename, directories made on the way — so a loaded configuration is created exactly as the editor creates one itself. Symbolic links and anything else that is not a regular file stay behind: a configuration is files.
The destination is dest/.turbo-go, dest being the working directory. If it already exists, nothing is done and ErrExists says so. A project's configuration is a decision the project made; the editor does not overwrite it because a URL was typed. Move it away first.
GIT_TERMINAL_PROMPT=0 is set on every git call: this runs from a command line that asked for a URL, and a private repository is reported, not waited on.
Public API
| Name | What it does |
|---|---|
Parse(url) (Source, error) |
Reads a forge's URL into host, owner, repo, ref, path |
Source.CloneURLs() []string |
The clone addresses to try, most likely first |
Load(ctx, profile, url, dest) (Result, error) |
Parse, find the repository, clone the ref, copy the editor's directory into dest |
Result |
The remote that answered, the ref, the directory created, the files in it |
ErrNoGit, ErrExists, ErrNotFound |
No git; the project already has the directory; the repository has none where the URL points |
Tests
go test ./configrepo/
TURBO_CORE_NETWORK=1 go test ./configrepo/ # also the real thing, against rickub
sampleRepo builds a repository on disk with git init — two branches, one of them feature/x — and loadFrom is pointed at it as a remote, which git accepts as readily as a URL. The network test is skipped unless asked for: the suite must pass on a machine with no network.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|