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/pages_test.go +25
| @@ -107,6 +107,31 @@ func TestSiteIsServedFromThePushedBranch(t *testing.T) { | |||
| 107 | 107 | } | |
| 108 | 108 | } | |
| 109 | 109 | ||
| 110 | // TestHeaderOffersSiteOnlyAfterADeploy: the Site link is a promise the instance | ||
| 111 | // can keep. Before the pages branch exists it would lead to a 404, so the | ||
| 112 | // repository header does not offer it; after the first deploy it appears on | ||
| 113 | // every repository tab. | ||
| 114 | func TestHeaderOffersSiteOnlyAfterADeploy(t *testing.T) { | ||
| 115 | t.Parallel() | ||
| 116 | f := pagesFixture(t) | ||
| 117 | f.publish(f.repo) | ||
| 118 | |||
| 119 | if code, body := f.get("/r/" + f.repo); code != 200 || strings.Contains(body, "Site</a>") { | ||
| 120 | t.Fatalf("header offers Site before a deploy: %d", code) | ||
| 121 | } | ||
| 122 | |||
| 123 | f.pushPages(t, map[string]string{"index.html": "hello\n"}) | ||
| 124 | |||
| 125 | want := "http://" + f.repo + "." + pagesDomain | ||
| 126 | code, body := f.get("/r/" + f.repo + "/code") | ||
| 127 | if code != 200 { | ||
| 128 | t.Fatalf("GET code = %d", code) | ||
| 129 | } | ||
| 130 | if !strings.Contains(body, `href="`+want+`"`) || !strings.Contains(body, "Site</a>") { | ||
| 131 | t.Fatalf("code header does not link the deployed site %q", want) | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 110 | 135 | // TestSitePushIsLiveWithinFiveSeconds checks that a push is live within five | |
| 111 | 136 | // seconds, and it is measured rather than assumed: push, then poll the site. | |
| 112 | 137 | func TestSitePushIsLiveWithinFiveSeconds(t *testing.T) { | |