• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

coccyx / gogen / 22638190328

03 Mar 2026 06:57PM UTC coverage: 71.666% (+6.3%) from 65.363%
22638190328

Pull #77

github

coccyx
Bump version to 0.13.0 for dev release

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pull Request #77: New UI Login feature

587 of 681 new or added lines in 19 files covered. (86.2%)

6 existing lines in 4 files now uncovered.

2762 of 3854 relevant lines covered (71.67%)

542.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/internal/github.go
1
package internal
2

3
// Mostly from https://jacobmartins.com/2016/02/29/getting-started-with-oauth2-in-go/
4

5
import (
6
        "context"
7
        "net/http"
8
        "os"
9
        "path/filepath"
10

11
        uuid "github.com/satori/go.uuid"
12
        "github.com/skratchdot/open-golang/open"
13

14
        log "github.com/coccyx/gogen/logger"
15
        "github.com/google/go-github/github"
16
        "golang.org/x/oauth2"
17
        githuboauth "golang.org/x/oauth2/github"
18
)
19

20
var (
21
        gitHubClientID     string // Passed in during the build process
22
        gitHubClientSecret string // Passed in during the build process
23
        oauthConf          = &oauth2.Config{
24
                RedirectURL:  "http://localhost:46436/GitHubCallback",
25
                ClientID:     gitHubClientID,
26
                ClientSecret: gitHubClientSecret,
27
                Endpoint:     githuboauth.Endpoint,
28
                Scopes:       []string{"gist"},
29
        }
30
        // Some random string, random for each request
31
        oauthStateString = uuid.NewV4().String()
32
)
33

34
// GitHub allows posting gists to a user's GitHub
35
type GitHub struct {
36
        done   chan int
37
        token  string
38
        client *github.Client
39
}
40

41
// NewGitHub returns a GitHub object, with a set auth token
42
func NewGitHub(requireauth bool) *GitHub {
×
43
        gh := new(GitHub)
×
44
        gh.done = make(chan int)
×
45

×
46
        log.Infof("GitHub OAuth Client ID: %s", gitHubClientID)
×
47

×
48
        if oauthConf.ClientID == "" {
×
49
                oauthConf.ClientID = os.Getenv("GITHUB_OAUTH_CLIENT_ID")
×
50
        }
×
51
        if oauthConf.ClientSecret == "" {
×
52
                oauthConf.ClientSecret = os.Getenv("GITHUB_OAUTH_CLIENT_SECRET")
×
53
        }
×
54

55
        tokenFile := filepath.Join(os.ExpandEnv("$GOGEN_HOME"), ".githubtoken")
×
56
        _, err := os.Stat(tokenFile)
×
57
        if err == nil {
×
NEW
58
                buf, err := os.ReadFile(tokenFile)
×
59
                if err != nil {
×
60
                        log.Fatalf("Error reading from file %s: %s", tokenFile, err)
×
61
                }
×
62
                gh.token = string(buf)
×
63
                log.Debugf("Getting GitHub token '%s' from file", gh.token)
×
64
        } else if requireauth {
×
65
                if !os.IsNotExist(err) {
×
66
                        log.Fatalf("Unexpected error accessing %s: %s", tokenFile, err)
×
67
                }
×
68
                http.HandleFunc("/GitHubLogin", gh.handleGitHubLogin)
×
69
                open.Run("http://localhost:46436/GitHubLogin")
×
70
                http.HandleFunc("/GitHubCallback", gh.handleGitHubCallback)
×
71
                go http.ListenAndServe(":46436", nil)
×
72
                <-gh.done
×
73
                log.Debugf("Getting GitHub token '%s' from oauth", gh.token)
×
74

×
NEW
75
                err = os.WriteFile(tokenFile, []byte(gh.token), 400)
×
76
                if err != nil {
×
77
                        log.Fatalf("Error writing token to file %s: %s", tokenFile, err)
×
78
                }
×
79
        }
80
        if len(gh.token) > 0 {
×
81
                ts := oauth2.StaticTokenSource(
×
82
                        &oauth2.Token{AccessToken: gh.token},
×
83
                )
×
NEW
84
                tc := oauth2.NewClient(context.Background(), ts)
×
85
                gh.client = github.NewClient(tc)
×
86
        } else {
×
87
                gh.client = github.NewClient(nil)
×
88
        }
×
89
        return gh
×
90
}
91

92
func (gh *GitHub) handleGitHubLogin(w http.ResponseWriter, r *http.Request) {
×
93
        url := oauthConf.AuthCodeURL(oauthStateString)
×
94
        http.Redirect(w, r, url, http.StatusTemporaryRedirect)
×
95
}
×
96

97
func (gh *GitHub) handleGitHubCallback(w http.ResponseWriter, r *http.Request) {
×
98
        state := r.FormValue("state")
×
99
        if state != oauthStateString {
×
100
                log.Errorf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
×
101
                http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
×
102
                return
×
103
        }
×
104

105
        code := r.FormValue("code")
×
NEW
106
        token, err := oauthConf.Exchange(context.Background(), code)
×
107
        if err != nil {
×
108
                log.Errorf("Code exchange failed with '%s'\n", err)
×
109
                http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
×
110
                return
×
111
        }
×
112

113
        gh.token = token.AccessToken
×
114
        gh.done <- 1
×
115
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc