Skip to content

Commit f6d8b75

Browse files
committed
Prompt user to create repo when missing
1 parent 2f148fa commit f6d8b75

File tree

9 files changed

+144
-29
lines changed

9 files changed

+144
-29
lines changed

bookmarkActionHandlers.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gobookmarks
22

33
import (
4+
"errors"
45
"fmt"
56
"github.com/gorilla/sessions"
67
"golang.org/x/oauth2"
@@ -30,7 +31,25 @@ func BookmarksEditSaveAction(w http.ResponseWriter, r *http.Request) error {
3031
return fmt.Errorf("bookmark modified concurrently")
3132
}
3233

34+
repoName := RepoName
35+
if r.PostFormValue("repoName") != "" {
36+
repoName = r.PostFormValue("repoName")
37+
}
38+
if r.PostFormValue("createRepo") == "1" {
39+
if err := ActiveProvider.CreateRepo(r.Context(), login, token, repoName); err != nil {
40+
return renderCreateRepoPrompt(w, r, repoName, text, branch, ref, sha, err)
41+
}
42+
RepoName = repoName
43+
if err := CreateBookmarks(r.Context(), login, token, branch, text); err != nil {
44+
return fmt.Errorf("createBookmark error: %w", err)
45+
}
46+
return nil
47+
}
48+
3349
if err := UpdateBookmarks(r.Context(), login, token, ref, branch, text, curSha); err != nil {
50+
if errors.Is(err, ErrRepoNotFound) {
51+
return renderCreateRepoPrompt(w, r, repoName, text, branch, ref, sha, nil)
52+
}
3453
return fmt.Errorf("updateBookmark error: %w", err)
3554
}
3655
return nil
@@ -90,3 +109,29 @@ func CategoryEditSaveAction(w http.ResponseWriter, r *http.Request) error {
90109
}
91110
return nil
92111
}
112+
113+
func renderCreateRepoPrompt(w http.ResponseWriter, r *http.Request, repoName, text, branch, ref, sha string, err error) error {
114+
data := struct {
115+
*CoreData
116+
RepoName string
117+
Text string
118+
Branch string
119+
Ref string
120+
Sha string
121+
Error string
122+
}{
123+
CoreData: r.Context().Value(ContextValues("coreData")).(*CoreData),
124+
RepoName: repoName,
125+
Text: text,
126+
Branch: branch,
127+
Ref: ref,
128+
Sha: sha,
129+
}
130+
if err != nil {
131+
data.Error = err.Error()
132+
}
133+
if tplErr := GetCompiledTemplates(NewFuncs(r)).ExecuteTemplate(w, "createRepo.gohtml", data); tplErr != nil {
134+
return fmt.Errorf("template: %w", tplErr)
135+
}
136+
return ErrHandled
137+
}

cmd/gobookmarks/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"crypto/x509/pkix"
1010
"encoding/json"
1111
"encoding/pem"
12+
"errors"
1213
"flag"
1314
"fmt"
1415
. "github.com/arran4/gobookmarks"
@@ -136,6 +137,7 @@ func main() {
136137

137138
UseCssColumns = cfg.CssColumns
138139
Namespace = cfg.Namespace
140+
RepoName = GetBookmarksRepoName()
139141
SiteTitle = cfg.Title
140142
if cfg.GitServer != "" {
141143
GitServer = cfg.GitServer
@@ -206,6 +208,7 @@ func main() {
206208

207209
log.Printf("gobookmarks: %s, commit %s, built at %s", version, commit, date)
208210
SetVersion(version, commit, date)
211+
RepoName = GetBookmarksRepoName()
209212
log.Printf("Redirect URL configured to: %s", redirectUrl)
210213
log.Println("Server started on http://localhost:8080")
211214
log.Println("Server started on https://localhost:8443")
@@ -342,6 +345,9 @@ func runHandlerChain(chain ...any) func(http.ResponseWriter, *http.Request) {
342345
each(w, r)
343346
case func(http.ResponseWriter, *http.Request) error:
344347
if err := each(w, r); err != nil {
348+
if errors.Is(err, ErrHandled) {
349+
return
350+
}
345351
type ErrorData struct {
346352
*CoreData
347353
Error string

data_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ func TestExecuteTemplates(t *testing.T) {
140140
*CoreData
141141
Error string
142142
}{baseData.CoreData, "boom"}},
143+
{"createRepo", "createRepo.gohtml", struct {
144+
*CoreData
145+
RepoName string
146+
Text string
147+
Branch string
148+
Ref string
149+
Sha string
150+
Error string
151+
}{baseData.CoreData, "MyBookmarks", "text", "main", "ref", "sha", ""}},
143152
}
144153

145154
for _, tt := range pages {

errors.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package gobookmarks
2+
3+
import "errors"
4+
5+
// ErrRepoNotFound indicates that the bookmarks repository does not exist.
6+
var ErrRepoNotFound = errors.New("repository not found")
7+
8+
// ErrHandled is returned by handlers when they have already written
9+
// a response and no further handlers should run.
10+
var ErrHandled = errors.New("handled")

provider.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ type Provider interface {
3535
GetCommits(ctx context.Context, user string, token *oauth2.Token) ([]*Commit, error)
3636
GetBookmarks(ctx context.Context, user, ref string, token *oauth2.Token) (string, string, error)
3737
UpdateBookmarks(ctx context.Context, user string, token *oauth2.Token, sourceRef, branch, text, expectSHA string) error
38-
CreateBookmarks(ctx context.Context, user string, token *oauth2.Token, branch, text string) error
39-
DefaultServer() string
38+
CreateBookmarks(ctx context.Context, user string, token *oauth2.Token, branch, text string) error
39+
CreateRepo(ctx context.Context, user string, token *oauth2.Token, name string) error
40+
DefaultServer() string
4041
}
4142

4243
var providers = map[string]Provider{}

provider_github.go

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -141,35 +141,31 @@ func (p GitHubProvider) GetBookmarks(ctx context.Context, user, ref string, toke
141141

142142
var commitAuthor = &github.CommitAuthor{Name: SP("Gobookmarks"), Email: SP("Gobookmarks@arran.net.au")}
143143

144-
func (p GitHubProvider) getDefaultBranch(ctx context.Context, user string, client *github.Client, branch string) (string, bool, error) {
144+
func (p GitHubProvider) getDefaultBranch(ctx context.Context, user string, client *github.Client, branch string) (string, error) {
145145
rep, resp, err := client.Repositories.Get(ctx, user, RepoName)
146-
created := false
147146
if resp != nil && resp.StatusCode == 404 {
148-
rep, err = p.createRepo(ctx, user, client)
149-
if err != nil {
150-
log.Printf("github createRepo: %v", err)
151-
return "", created, err
152-
}
153-
created = true
147+
return "", ErrRepoNotFound
154148
}
155149
if err != nil {
156150
log.Printf("github getDefaultBranch: %v", err)
157-
return "", created, fmt.Errorf("Repositories.Get: %w", err)
151+
return "", fmt.Errorf("Repositories.Get: %w", err)
158152
}
159153
if rep.DefaultBranch != nil {
160154
branch = *rep.DefaultBranch
161155
} else {
162156
branch = "main"
163157
}
164-
return branch, created, nil
158+
return branch, nil
165159
}
166160

167-
func (p GitHubProvider) createRepo(ctx context.Context, user string, client *github.Client) (*github.Repository, error) {
161+
func (p GitHubProvider) CreateRepo(ctx context.Context, user string, token *oauth2.Token, name string) error {
162+
client := p.client(ctx, token)
163+
RepoName = name
168164
rep := &github.Repository{Name: &RepoName, Description: SP("Personal bookmarks"), Private: BP(true)}
169165
rep, _, err := client.Repositories.Create(ctx, "", rep)
170166
if err != nil {
171167
log.Printf("github createRepo: %v", err)
172-
return nil, fmt.Errorf("Repositories.Create: %w", err)
168+
return fmt.Errorf("Repositories.Create: %w", err)
173169
}
174170
_, _, err = client.Repositories.CreateFile(ctx, user, RepoName, "readme.md", &github.RepositoryContentFileOptions{
175171
Message: SP("Auto create from web"),
@@ -180,9 +176,10 @@ See . https://github.com/arran4/gobookmarks `),
180176
})
181177
if err != nil {
182178
log.Printf("github createRepo readme: %v", err)
183-
return nil, fmt.Errorf("CreateReadme: %w", err)
179+
return fmt.Errorf("CreateReadme: %w", err)
184180
}
185-
return rep, nil
181+
_ = rep
182+
return nil
186183
}
187184

188185
func (p GitHubProvider) createRef(ctx context.Context, user string, client *github.Client, sourceRef, branchRef string) error {
@@ -204,7 +201,7 @@ func (p GitHubProvider) createRef(ctx context.Context, user string, client *gith
204201

205202
func (p GitHubProvider) UpdateBookmarks(ctx context.Context, user string, token *oauth2.Token, sourceRef, branch, text, expectSHA string) error {
206203
client := p.client(ctx, token)
207-
defaultBranch, created, err := p.getDefaultBranch(ctx, user, client, branch)
204+
defaultBranch, err := p.getDefaultBranch(ctx, user, client, branch)
208205
if err != nil {
209206
return err
210207
}
@@ -215,9 +212,6 @@ func (p GitHubProvider) UpdateBookmarks(ctx context.Context, user string, token
215212
if sourceRef == "" {
216213
sourceRef = branchRef
217214
}
218-
if created {
219-
return p.CreateBookmarks(ctx, user, token, branch, text)
220-
}
221215
_, grefResp, err := client.Git.GetRef(ctx, user, RepoName, branchRef)
222216
if err != nil && grefResp.StatusCode != 404 {
223217
log.Printf("github UpdateBookmarks getRef: %v", err)
@@ -231,11 +225,7 @@ func (p GitHubProvider) UpdateBookmarks(ctx context.Context, user string, token
231225
}
232226
contents, _, resp, err := client.Repositories.GetContents(ctx, user, RepoName, "bookmarks.txt", &github.RepositoryContentGetOptions{Ref: branchRef})
233227
if resp != nil && resp.StatusCode == 404 {
234-
if _, err := p.createRepo(ctx, user, client); err != nil {
235-
log.Printf("github UpdateBookmarks create repo: %v", err)
236-
return fmt.Errorf("CreateRepo: %w", err)
237-
}
238-
return p.CreateBookmarks(ctx, user, token, branch, text)
228+
return ErrRepoNotFound
239229
}
240230
if err != nil {
241231
log.Printf("github UpdateBookmarks get contents: %v", err)
@@ -266,7 +256,7 @@ func (p GitHubProvider) CreateBookmarks(ctx context.Context, user string, token
266256
client := p.client(ctx, token)
267257
if branch == "" {
268258
var err error
269-
branch, _, err = p.getDefaultBranch(ctx, user, client, branch)
259+
branch, err = p.getDefaultBranch(ctx, user, client, branch)
270260
if err != nil {
271261
log.Printf("github CreateBookmarks default branch: %v", err)
272262
return err

provider_gitlab.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ func (GitLabProvider) UpdateBookmarks(ctx context.Context, user string, token *o
167167
}
168168
_, _, err = c.RepositoryFiles.UpdateFile(user+"/"+RepoName, "bookmarks.txt", opt)
169169
if err != nil {
170+
if respErr, ok := err.(*gitlab.ErrorResponse); ok && respErr.Response != nil && respErr.Response.StatusCode == http.StatusNotFound {
171+
return ErrRepoNotFound
172+
}
170173
log.Printf("gitlab UpdateBookmarks: %v", err)
171174
return err
172175
}
@@ -188,7 +191,27 @@ func (GitLabProvider) CreateBookmarks(ctx context.Context, user string, token *o
188191
}
189192
_, _, err = c.RepositoryFiles.CreateFile(user+"/"+RepoName, "bookmarks.txt", opt)
190193
if err != nil {
191-
log.Printf("gitlab CreateBookmarks: %v", err)
194+
if respErr, ok := err.(*gitlab.ErrorResponse); ok && respErr.Response != nil && respErr.Response.StatusCode == http.StatusNotFound {
195+
return ErrRepoNotFound
196+
}
197+
if err != nil {
198+
log.Printf("gitlab CreateBookmarks: %v", err)
199+
}
200+
}
201+
return err
202+
}
203+
204+
func (p GitLabProvider) CreateRepo(ctx context.Context, user string, token *oauth2.Token, name string) error {
205+
c, err := GitLabProvider{}.client(token)
206+
if err != nil {
207+
return err
192208
}
209+
RepoName = name
210+
_, _, err = c.Projects.CreateProject(&gitlab.CreateProjectOptions{
211+
Name: gitlab.String(RepoName),
212+
Description: gitlab.String("Personal bookmarks"),
213+
Visibility: gitlab.Visibility(gitlab.PrivateVisibility),
214+
InitializeWithReadme: gitlab.Ptr(true),
215+
})
193216
return err
194217
}

repo.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@ package gobookmarks
22

33
var RepoName = GetBookmarksRepoName()
44

5+
// GetBookmarksRepoName returns the repository name based on the current
6+
// configuration and build mode. When running a development build the name is
7+
// suffixed with "-dev". The Namespace value is appended if supplied.
58
func GetBookmarksRepoName() string {
9+
name := "MyBookmarks"
10+
if version == "dev" {
11+
name += "-dev"
12+
}
613
if Namespace != "" {
7-
return "MyBookmarks-" + Namespace
14+
name += "-" + Namespace
815
}
9-
return "MyBookmarks"
16+
return name
1017
}

templates/createRepo.gohtml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{ template "head" $ }}
2+
{{ if $.Error }}
3+
<p style="color: #FF0000">Error: {{ $.Error }}</p>
4+
{{ end }}
5+
<form method="post" action="/edit">
6+
<input type="hidden" name="task" value="Save" />
7+
<input type="hidden" name="text" value="{{ $.Text }}" />
8+
<input type="hidden" name="branch" value="{{ $.Branch }}" />
9+
<input type="hidden" name="ref" value="{{ $.Ref }}" />
10+
<input type="hidden" name="sha" value="{{ $.Sha }}" />
11+
<input type="hidden" name="createRepo" value="1" />
12+
Repository Name: <input type="text" name="repoName" value="{{ $.RepoName }}" /><br/>
13+
<input type="submit" value="Create Repo and Retry" />
14+
</form>
15+
<form method="post" action="/edit">
16+
<input type="hidden" name="task" value="Save" />
17+
<input type="hidden" name="text" value="{{ $.Text }}" />
18+
<input type="hidden" name="branch" value="{{ $.Branch }}" />
19+
<input type="hidden" name="ref" value="{{ $.Ref }}" />
20+
<input type="hidden" name="sha" value="{{ $.Sha }}" />
21+
<input type="submit" value="Retry" />
22+
</form>
23+
{{ template "tail" $ }}
24+

0 commit comments

Comments
 (0)