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

badges / shields / 27493278414

13 Jun 2026 07:03PM UTC coverage: 97.957% (-0.03%) from 97.987%
27493278414

push

github

web-flow
Add telemetry when GitHub token is removed (#11922)

5850 of 6116 branches covered (95.65%)

30 of 35 new or added lines in 3 files covered. (85.71%)

13 existing lines in 1 file now uncovered.

49429 of 50460 relevant lines covered (97.96%)

138.1 hits per line

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

53.21
/services/github/github-constellation.js
1
import { AuthHelper } from '../../core/base-service/auth-helper.js'
3✔
2
import SqlTokenPersistence from '../../core/token-pooling/sql-token-persistence.js'
3✔
3
import log from '../../core/server/log.js'
3✔
4
import GithubApiProvider from './github-api-provider.js'
3✔
5
import { setRoutes as setAcceptorRoutes } from './auth/acceptor.js'
3✔
6

3✔
7
// Convenience class with all the stuff related to the Github API and its
3✔
8
// authorization tokens, to simplify server initialization.
3✔
9
class GithubConstellation {
3✔
10
  static _createOauthHelper(config) {
3✔
11
    return new AuthHelper(
19✔
12
      {
19✔
13
        userKey: 'gh_client_id',
19✔
14
        passKey: 'gh_client_secret',
19✔
15
        authorizedOrigins: ['https://api.github.com'],
19✔
16
        isRequired: true,
19✔
17
      },
19✔
18
      config,
19✔
19
    )
19✔
20
  }
19✔
21

3✔
22
  constructor(config) {
3✔
23
    this._debugEnabled = config.service.debug.enabled
18✔
24
    this._debugIntervalSeconds = config.service.debug.intervalSeconds
18✔
25
    this._metricsIntervalSeconds = config.metricsIntervalSeconds
18✔
26

18✔
27
    let authType = GithubApiProvider.AUTH_TYPES.NO_AUTH
18✔
28

18✔
29
    const { postgres_url: pgUrl, gh_token: globalToken } = config.private
18✔
30
    if (pgUrl) {
18✔
31
      log.log('Github Token persistence configured with pgUrl')
10✔
32
      this.persistence = new SqlTokenPersistence({
10✔
33
        url: pgUrl,
10✔
34
        table: 'github_user_tokens',
10✔
35
      })
10✔
36
      authType = GithubApiProvider.AUTH_TYPES.TOKEN_POOL
10✔
37
    }
10✔
38

18✔
39
    if (globalToken) {
18✔
40
      authType = GithubApiProvider.AUTH_TYPES.GLOBAL_TOKEN
12✔
41
    }
12✔
42

18✔
43
    log.log(`Github using auth type: ${authType}`)
18✔
44

18✔
45
    this.apiProvider = new GithubApiProvider({
18✔
46
      baseUrl: config.service.baseUri,
18✔
47
      globalToken,
18✔
48
      authType,
18✔
49
      onTokenInvalidated: tokenString => this.onTokenInvalidated(tokenString),
18✔
50
      restApiVersion: config.service.restApiVersion,
18✔
51
    })
18✔
52

18✔
53
    this.oauthHelper = this.constructor._createOauthHelper(config)
18✔
54
  }
18✔
55

3✔
56
  scheduleDebugLogging() {
3✔
57
    if (this._debugEnabled) {
×
58
      this.debugInterval = setInterval(() => {
×
59
        const debugInfo = this.apiProvider.getTokenDebugInfo()
×
60
        log.log(debugInfo)
×
61
      }, 1000 * this._debugIntervalSeconds)
×
62
    }
×
63
  }
×
64

3✔
65
  scheduleMetricsCollection() {
3✔
66
    if (this.metricInstance) {
×
67
      this.metricsInterval = setInterval(() => {
×
68
        const debugInfo = this.apiProvider.getTokenDebugInfo()
×
69
        this.metricInstance.noteGithubTokenPoolMetrics(debugInfo)
×
70
      }, 1000 * this._metricsIntervalSeconds)
×
71
    }
×
72
  }
×
73

3✔
74
  async initialize(server, metricInstance) {
3✔
75
    if (this.apiProvider.authType !== GithubApiProvider.AUTH_TYPES.TOKEN_POOL) {
9✔
76
      return
9✔
77
    }
9✔
78

×
79
    this.metricInstance = metricInstance
×
NEW
80
    this.apiProvider.metricInstance = metricInstance
×
81

×
82
    this.scheduleDebugLogging()
×
83
    this.scheduleMetricsCollection()
×
84

×
85
    if (!this.persistence) {
×
86
      return
×
87
    }
×
88

×
89
    let tokens = []
×
90
    try {
×
91
      tokens = await this.persistence.initialize()
×
92
    } catch (e) {
×
93
      log.error(e)
×
94
    }
×
95

×
96
    tokens.forEach(tokenString => {
×
97
      this.apiProvider.addToken(tokenString)
×
98
    })
×
99

×
100
    if (this.oauthHelper.isConfigured) {
×
101
      setAcceptorRoutes({
×
102
        server,
×
103
        authHelper: this.oauthHelper,
×
104
        onTokenAccepted: tokenString => this.onTokenAdded(tokenString),
×
105
      })
×
106
    }
×
107
  }
9✔
108

3✔
109
  onTokenAdded(tokenString) {
3✔
110
    if (!this.persistence) {
×
111
      throw Error('Token persistence is not configured')
×
112
    }
×
113
    this.apiProvider.addToken(tokenString)
×
114
    process.nextTick(async () => {
×
115
      try {
×
116
        await this.persistence.noteTokenAdded(tokenString)
×
117
      } catch (e) {
×
118
        log.error(e)
×
119
      }
×
120
    })
×
121
  }
×
122

3✔
123
  onTokenInvalidated(tokenString) {
3✔
124
    if (this.persistence) {
×
125
      process.nextTick(async () => {
×
126
        try {
×
127
          await this.persistence.noteTokenRemoved(tokenString)
×
128
        } catch (e) {
×
129
          log.error(e)
×
130
        }
×
131
      })
×
132
    }
×
133
  }
×
134

3✔
135
  async stop() {
3✔
136
    if (this.debugInterval) {
9!
137
      clearInterval(this.debugInterval)
×
138
      this.debugInterval = undefined
×
139
    }
×
140

9✔
141
    if (this.metricsInterval) {
9!
142
      clearInterval(this.metricsInterval)
×
143
      this.metricsInterval = undefined
×
144
    }
×
145

9✔
146
    if (this.persistence) {
9✔
147
      try {
8✔
148
        await this.persistence.stop()
8✔
149
      } catch (e) {
8!
150
        log.error(e)
×
151
      }
×
152
    }
8✔
153
  }
9✔
154
}
3✔
155

3✔
156
export default GithubConstellation
3✔
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