Skip to content

Commit

Permalink
allow PAT
Browse files Browse the repository at this point in the history
  • Loading branch information
nzin-alayacare committed Mar 2, 2025
1 parent 1697fd2 commit bb71538
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 21 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Goliac v0.15.7

- allow to use a PAT (Personal Access Token) to run Goliac (in particular useful to scaffold)

## Goliac v0.15.6

- new property to specify default branch for each repository
Expand Down
1 change: 1 addition & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ You can run the goliac server as a service or a docker container. It needs sever
| GOLIAC_GITHUB_APP_PRIVATE_KEY_FILE | | (mandatory) path to private key |
| GOLIAC_GITHUB_TEAM_APP_ID | | (optional) dedicated app id of Goliac GitHub App for goliac teams repo (see security.md) |
| GOLIAC_GITHUB_TEAM_APP_PRIVATE_KEY_FILE | | (optional) dedicated path to private key for goliac teams repo (see security.md) |
| GOLIAC_GITHUB_PERSONAL_ACCESS_TOKEN | | (optional) personal access token to use instead of the GitHub App |
| GOLIAC_EMAIL | [email protected] | author name used by Goliac to commit (Codeowners) |
| GOLIAC_GITHUB_CONCURRENT_THREADS | 5 | You can increase, like '10' |
| GOLIAC_GITHUB_CACHE_TTL | 86400 | GitHub remote cache seconds retention |
Expand Down
11 changes: 11 additions & 0 deletions docs/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ As a Github org admin, in GitHub:
- Go to the left tab "Install App"
- Click on "Install"

### Alternative: use a personal access token

If you don't have the possibility to create a Github App, you can use a personal access token.
If you only need to scaffold, you will need a personal access token with
- `read:org` scope (under `admin:org` category)

If you want to use the full Goliac capabilities, you will need a personal access token with
- `read:org` and `write:org` scope (under `admin:org` category)

You will need to export the `GOLIAC_GITHUB_PERSONAL_ACCESS_TOKEN` env variable (instead of `GOLIAC_GITHUB_APP_ID`, `GOLIAC_GITHUB_APP_PRIVATE_KEY_FILE`) in the following examples.

### Get the Goliac binary

```shell
Expand Down
1 change: 1 addition & 0 deletions internal/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var Config = struct {
GithubAppPrivateKeyFile string `env:"GOLIAC_GITHUB_APP_PRIVATE_KEY_FILE" envDefault:"github-app-private-key.pem"`
GithubTeamAppID int64 `env:"GOLIAC_GITHUB_TEAM_APP_ID"`
GithubTeamAppPrivateKeyFile string `env:"GOLIAC_GITHUB_TEAM_APP_PRIVATE_KEY_FILE"`
GithubPersonalAccessToken string `env:"GOLIAC_GITHUB_PERSONAL_ACCESS_TOKEN"`
GoliacEmail string `env:"GOLIAC_EMAIL" envDefault:"[email protected]"`
GoliacTeamOwnerSuffix string `env:"GOLIAC_TEAM_OWNER_SUFFIX" envDefault:"-goliac-owners"`

Expand Down
55 changes: 34 additions & 21 deletions internal/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type GitHubClientImpl struct {
installationID int64
appSlug string
privateKey []byte
patToken string // if not "" we use the personal access token
accessToken string
httpClient *http.Client
tokenExpiration time.Time
Expand All @@ -50,6 +51,13 @@ type AuthorizedTransport struct {
func (t *AuthorizedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
t.client.mu.Lock()

// Use the personal access token if available
if t.client.patToken != "" {
req.Header.Add("Authorization", "token "+t.client.patToken)
t.client.mu.Unlock()
return http.DefaultTransport.RoundTrip(req)
}

// Refresh the access token if necessary
if t.client.accessToken == "" || time.Until(t.client.tokenExpiration) < 5*time.Minute {
token, err := t.client.createJWT()
Expand Down Expand Up @@ -90,7 +98,7 @@ func (t *AuthorizedTransport) RoundTrip(req *http.Request) (*http.Response, erro
* "private-key.pem",
* )
*/
func NewGitHubClientImpl(githubServer, organizationName string, appID int64, privateKeyFile string) (GitHubClient, error) {
func NewGitHubClientImpl(githubServer, organizationName string, appID int64, privateKeyFile string, patToken string) (GitHubClient, error) {
privateKey, err := os.ReadFile(privateKeyFile)
if err != nil {
return nil, err
Expand All @@ -100,32 +108,37 @@ func NewGitHubClientImpl(githubServer, organizationName string, appID int64, pri
gitHubServer: githubServer,
appID: appID,
privateKey: privateKey,
patToken: patToken,
}

// create JWT
token, err := client.createJWT()
if err != nil {
return nil, err
}
// If a personal access token is not provided, we need to find the installation ID
if client.patToken == "" {

// retrieve all installations for the authenticated app
installations, err := client.getInstallations(token)
if err != nil {
return nil, err
}
// create JWT
token, err := client.createJWT()
if err != nil {
return nil, err
}

// find the installation ID for the given organization
for _, installation := range installations {
logrus.Debugf("Found installation %s with id %d for organization: %s", installation.AppSlug, installation.ID, organizationName)
if strings.EqualFold(installation.Account.Login, organizationName) && installation.AppId == appID {
client.installationID = installation.ID
client.appSlug = installation.AppSlug
break
// retrieve all installations for the authenticated app
installations, err := client.getInstallations(token)
if err != nil {
return nil, err
}
}

if client.installationID == 0 {
return nil, fmt.Errorf("installation not found for organization: %s", organizationName)
// find the installation ID for the given organization
for _, installation := range installations {
logrus.Debugf("Found installation %s with id %d for organization: %s", installation.AppSlug, installation.ID, organizationName)
if strings.EqualFold(installation.Account.Login, organizationName) && installation.AppId == appID {
client.installationID = installation.ID
client.appSlug = installation.AppSlug
break
}
}

if client.installationID == 0 {
return nil, fmt.Errorf("installation not found for organization: %s", organizationName)
}
}

transport := &AuthorizedTransport{
Expand Down
2 changes: 2 additions & 0 deletions internal/goliac.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func NewGoliacImpl() (Goliac, error) {
config.Config.GithubAppOrganization,
config.Config.GithubAppID,
config.Config.GithubAppPrivateKeyFile,
config.Config.GithubPersonalAccessToken,
)
if err != nil {
return nil, err
Expand All @@ -75,6 +76,7 @@ func NewGoliacImpl() (Goliac, error) {
config.Config.GithubAppOrganization,
config.Config.GithubTeamAppID,
config.Config.GithubTeamAppPrivateKeyFile,
config.Config.GithubPersonalAccessToken,
)
if err != nil {
return nil, err
Expand Down
1 change: 1 addition & 0 deletions internal/scaffold.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func NewScaffold() (*Scaffold, error) {
config.Config.GithubAppOrganization,
config.Config.GithubAppID,
config.Config.GithubAppPrivateKeyFile,
config.Config.GithubPersonalAccessToken,
)

if err != nil {
Expand Down

0 comments on commit bb71538

Please sign in to comment.