This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new f72868cd3 fix: disable API client caching for GitHub App connections
(#8850)
f72868cd3 is described below
commit f72868cd3423bdab0416f8611d85bb1d98699772
Author: fxgagnon <[email protected]>
AuthorDate: Sat Apr 25 09:00:34 2026 -0400
fix: disable API client caching for GitHub App connections (#8850)
GitHub App installation tokens expire after ~1 hour, causing admin APIs
(remote-scopes, test-connection) to fail silently after token expiry.
This fix overrides GetHash() to return empty string for GitHub App
connections,
disabling client caching and ensuring fresh tokens are fetched for each
admin request.
PAT connections continue to use default caching behavior.
Fixes #8847
---
backend/plugins/github/models/connection.go | 10 ++++++
backend/plugins/github/models/connection_test.go | 42 ++++++++++++++++++++++++
2 files changed, 52 insertions(+)
diff --git a/backend/plugins/github/models/connection.go
b/backend/plugins/github/models/connection.go
index 51034f67c..4489b70cb 100644
--- a/backend/plugins/github/models/connection.go
+++ b/backend/plugins/github/models/connection.go
@@ -134,6 +134,16 @@ func (connection GithubConnection) TableName() string {
return "_tool_github_connections"
}
+func (connection GithubConnection) GetHash() string {
+ if connection.AuthMethod == AppKey {
+ // GitHub App installation tokens expire after ~1 hour, disable
API client caching
+ // to ensure fresh tokens are fetched for admin APIs
(remote-scopes, test-connection)
+ return ""
+ }
+ // Use default caching for PAT connections (they don't expire)
+ return connection.BaseConnection.GetHash()
+}
+
func (connection *GithubConnection) MergeFromRequest(target *GithubConnection,
body map[string]interface{}) error {
modifiedConnection := GithubConnection{}
if err := helper.DecodeMapStruct(body, &modifiedConnection, true); err
!= nil {
diff --git a/backend/plugins/github/models/connection_test.go
b/backend/plugins/github/models/connection_test.go
index 41323b9b5..7d4c74673 100644
--- a/backend/plugins/github/models/connection_test.go
+++ b/backend/plugins/github/models/connection_test.go
@@ -18,6 +18,7 @@ limitations under the License.
package models
import (
+ "github.com/apache/incubator-devlake/core/models/common"
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
"testing"
@@ -238,3 +239,44 @@ func TestTokenTypeClassification(t *testing.T) {
assert.Equal(t, GithubTokenTypeFineGrained,
conn.typeIs("github_pat_123"))
assert.Equal(t, GithubTokenTypeUnknown, conn.typeIs("some_other_token"))
}
+
+func TestGithubConnection_GetHash(t *testing.T) {
+ tests := []struct {
+ name string
+ connection GithubConnection
+ want string
+ }{
+ {
+ name: "GitHub App connection should return empty hash
to disable caching",
+ connection: GithubConnection{
+ GithubConn: GithubConn{
+ MultiAuth: api.MultiAuth{
+ AuthMethod: AppKey,
+ },
+ },
+ },
+ want: "",
+ },
+ {
+ name: "PAT connection should use default hash",
+ connection: GithubConnection{
+ BaseConnection: api.BaseConnection{
+ Model: common.Model{
+ ID: 123,
+ },
+ },
+ GithubConn: GithubConn{
+ MultiAuth: api.MultiAuth{
+ AuthMethod: AccessToken,
+ },
+ },
+ },
+ want: "1230001-01-01 00:00:00 +0000 UTC", // ID + zero
UpdatedAt
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assert.Equalf(t, tt.want, tt.connection.GetHash(),
"GetHash()")
+ })
+ }
+}