vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Move Clone and Site into the repository header
816cc543d34ab52df01c62cfd8878ae70327a21f · Keenan Nemetz · 11d ago · parent a196e404
Move Clone and Site into the repository header The repository header now carries the controls that act on the repository as a whole. Clone appears once beside the name on every tab, with a copy button for the HTTPS URL and one for the SSH command. Site joins it only when a site would answer: pages are enabled, the repository is public, and its pages branch exists. The per-tab Clone popovers and the Overview/Code Site buttons are gone, so a tab's page header holds only that tab's own actions. Copy feedback now restores innerHTML, keeping the icon on an icon-only button and surviving a repeated click.
server/web/page.go +42−7
| @@ -30,6 +30,13 @@ type Page struct { | |||
| 30 | 30 | // CanAdmin reports whether the viewer may administer the repository named in | |
| 31 | 31 | // the request path. The repository header uses it to show Settings. | |
| 32 | 32 | CanAdmin bool | |
| 33 | // CloneURL and SSHURL are the two transports the repository header offers to | ||
| 34 | // copy. They are empty when the path does not name a repository. | ||
| 35 | CloneURL string | ||
| 36 | SSHURL string | ||
| 37 | // Site is the repository's deployed site URL. It is empty unless sites are | ||
| 38 | // enabled, the repository is public, and its pages branch exists. | ||
| 39 | Site string | ||
| 33 | 40 | // InstanceAdmin reports whether the viewer may use the instance-wide admin | |
| 34 | 41 | // surface. The global bar uses it to show Admin. | |
| 35 | 42 | InstanceAdmin bool | |
| @@ -72,13 +79,14 @@ func (w *Web) who(name string) string { | |||
| 72 | 79 | } | |
| 73 | 80 | ||
| 74 | 81 | // repoNav is the repository header's data: which repository, which tab is | |
| 75 | // current, whether anonymous readers can see it, and how many open issues and | ||
| 76 | // change requests its tabs should advertise. Visibility comes from the same ACL | ||
| 77 | // rule the handlers use, so the lock/globe icon cannot disagree with whether an | ||
| 78 | // anonymous clone works. Templates call it as | ||
| 79 | // {{template "repohead" (nav .Name "issues" .CanAdmin)}}. | ||
| 80 | func (w *Web) repoNav(name, tab string, canAdmin bool) repoNavData { | ||
| 81 | n := repoNavData{Name: name, Tab: tab, Private: true, CanAdmin: canAdmin} | ||
| 82 | // current, whether anonymous readers can see it, the clone transports and site | ||
| 83 | // to offer, and how many open issues and change requests its tabs should | ||
| 84 | // advertise. Visibility comes from the same ACL rule the handlers use, so the | ||
| 85 | // lock/globe icon cannot disagree with whether an anonymous clone works. | ||
| 86 | // Templates call it as | ||
| 87 | // {{template "repohead" (nav .Name "issues" .CanAdmin .CloneURL .SSHURL .Site)}}. | ||
| 88 | func (w *Web) repoNav(name, tab string, canAdmin bool, cloneURL, sshURL, site string) repoNavData { | ||
| 89 | n := repoNavData{Name: name, Tab: tab, Private: true, CanAdmin: canAdmin, CloneURL: cloneURL, SSHURL: sshURL, Site: site} | ||
| 82 | 90 | m, err := w.Repos.Open(name) | |
| 83 | 91 | if err != nil { | |
| 84 | 92 | return n | |
| @@ -114,12 +122,39 @@ func (w *Web) openCounts(m *repo.Meta) (issues, requests int) { | |||
| 114 | 122 | return issues, requests | |
| 115 | 123 | } | |
| 116 | 124 | ||
| 125 | // siteDeployed reports whether the repository actually has a site to link to. | ||
| 126 | // A configured pages domain is not enough: the site server only answers for a | ||
| 127 | // public repository's pages branch, so a link shown otherwise would lead to a | ||
| 128 | // 404. A missing branch is the normal state before the first deploy. | ||
| 129 | func (w *Web) siteDeployed(m *repo.Meta, acl *access.ACL) bool { | ||
| 130 | if w.Cfg == nil || strings.TrimSpace(w.Cfg.PagesDomain) == "" || acl == nil { | ||
| 131 | return false | ||
| 132 | } | ||
| 133 | if !acl.Can("", access.Read) { | ||
| 134 | return false | ||
| 135 | } | ||
| 136 | ref := strings.TrimSpace(w.Cfg.PagesRef) | ||
| 137 | if ref == "" { | ||
| 138 | ref = "pages" | ||
| 139 | } | ||
| 140 | full := "refs/heads/" + ref | ||
| 141 | refs, err := m.Git().ListRefs(full) | ||
| 142 | if err != nil { | ||
| 143 | return false | ||
| 144 | } | ||
| 145 | _, ok := refs[full] | ||
| 146 | return ok | ||
| 147 | } | ||
| 148 | |||
| 117 | 149 | type repoNavData struct { | |
| 118 | 150 | Name string | |
| 119 | 151 | Description string | |
| 120 | 152 | Tab string | |
| 121 | 153 | Private bool | |
| 122 | 154 | CanAdmin bool | |
| 155 | CloneURL string | ||
| 156 | SSHURL string | ||
| 157 | Site string | ||
| 123 | 158 | OpenIssues int | |
| 124 | 159 | OpenRequests int | |
| 125 | 160 | } | |