Skip to content

Commit ce2a0bf

Browse files
authored
feat(cli): add option to use private key via env var and also via file (#1)
1 parent 822afcb commit ce2a0bf

File tree

7 files changed

+136
-23
lines changed

7 files changed

+136
-23
lines changed

.github/CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Contributing
2+
3+
Thank you for investing your time in contributing to the project! Here are base instructions that should be followed if you want to contribute to this project.
4+
5+
## Language
6+
The only language that should be used in code, documentation and commits should be english.
7+
8+
## Pull requests
9+
10+
All changes made on the repository should be done via pull requests.
11+
12+
## Commits
13+
14+
Please follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0) rules on commit messages if you create commits via pull request to the Blueprint. These commits are used to create the changelog.

.github/workflows/ci-pull-request.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ jobs:
3333
go build -o "./dist/ghapp_${OS}_${ARCH}"
3434
done
3535
done
36+
- id: docker-setup
37+
name: docker-setup
38+
run: |
39+
docker buildx create \
40+
--use \
41+
--platform linux/amd64,linux/arm64
3642
- id: docker-build
3743
name: docker-build
3844
run: |

.github/workflows/ci.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,24 @@ jobs:
8383
docker login "${DOCKER_REGISTRY_HOST}" \
8484
--username "${DOCKER_REGISTRY_USERNAME}" \
8585
--password-stdin
86+
- id: docker-setup
87+
name: docker-setup
88+
run: |
89+
docker buildx create \
90+
--use \
91+
--platform linux/amd64,linux/arm64
8692
- id: docker-build
8793
name: docker-build
8894
run: |
8995
docker buildx build . \
9096
--push \
91-
--tag "${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_PATH}:${VERSION}"
97+
--tag "${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_PATH}:${VERSION}" \
98+
--platform=linux/amd64,linux/arm64
9299
- id: docker-build-latest
93100
name: docker-build-latest
94101
if: ${{ github.ref_type == 'tag' }}
95102
run: |
96103
docker buildx build . \
97104
--push \
98-
--tag "${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_PATH}:latest"
105+
--tag "${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_PATH}:latest" \
106+
--platform=linux/amd64,linux/arm64

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
FROM --platform=${TARGETPLATFORM} golang:alpine AS build
2+
13
FROM scratch AS cli
4+
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
25
ARG TARGETOS="linux"
36
ARG TARGETARCH="amd64"
47
COPY ./dist/ghapp_${TARGETOS}_${TARGETARCH} /ghapp

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,66 @@
11
# ghapp
2+
3+
[![ci](https://github.com/jhagestedt/ghapp/actions/workflows/ci.yml/badge.svg)](https://github.com/jhagestedt/ghapp/actions/workflows/ci.yml)
4+
5+
Lightweight `CLI` to create GitHub installation tokens for GitHub Apps.
6+
7+
## Install
8+
9+
The packaged binaries of `ghapp` can be found on the [releases](https://github.com/jhagestedt/ghapp/releases) of this repository.
10+
11+
### Linux
12+
13+
```bash
14+
curl -L "https://github.com/jhagestedt/ghapp/releases/latest/download/ghapp_linux_amd64" \
15+
-o ./ghapp && chmod +x ./ghapp
16+
```
17+
18+
### MacOS
19+
20+
```bash
21+
curl -L "https://github.com/jhagestedt/ghapp/releases/latest/download/ghapp_darwin_amd64" \
22+
-o ./ghapp && chmod +x ./ghapp
23+
```
24+
25+
## Usage
26+
27+
A GitHub App installation token can be created by the GitHub App id, the installation id and the private key like described in the [docs](https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps).
28+
29+
To create an installation token with `ghapp` the private key can be injected via file or as environment variable.
30+
31+
The GitHub App id and installation id can be passed as flags or environment variables.
32+
33+
```bash
34+
# load private key from file
35+
ghapp token --id 123 --install-id 123456 --private-key-file .ghapp-private-key.pem
36+
37+
# load private key from environment variable
38+
export GHAPP_PRIVATE_KEY=$(cat .ghapp-private-key.pem)
39+
ghapp token --id 123 --install-id 123456
40+
```
41+
42+
A usage on GitHub Actions could look like the following.
43+
44+
```yaml
45+
- id: setup-ghapp
46+
run: |
47+
curl -L "https://github.com/jhagestedt/ghapp/releases/latest/download/ghapp_linux_amd64" -o ./usr/local/bin/ghapp
48+
chmod +x ./usr/local/bin/ghapp
49+
- id: ghapp-install-token
50+
run: |
51+
GHAPP_INSTALL_TOKEN=$(ghapp token)
52+
echo "::set-output name=ghapp_install_token::${GHAPP_INSTALL_TOKEN}"
53+
env:
54+
GHAPP_ID: 123
55+
GHAPP_INSTALL_ID: 123456
56+
GHAPP_PRIVATE_KEY: ${{ secrets.GHAPP_PRIVATE_KEY }}
57+
```
58+
59+
## Docker
60+
61+
There is also a docker container published at [Dockerhub](https://hub.docker.com/repository/docker/jhagestedt/ghapp) that contains the binary that can be used with `docker run` to not install it locally.
62+
63+
```bash
64+
export GHAPP_PRIVATE_KEY=$(cat .ghapp-private-key.pem)
65+
docker run --rm -e GHAPP_PRIVATE_KEY jhagestedt/ghapp token --id 123 --installation-id 123456
66+
```

cmd/github.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import (
1111
)
1212

1313
var client = resty.New()
14-
var privateKeyPrefix = "-----BEGIN RSA PRIVATE KEY-----\n"
15-
var privateKeySuffix = "-----END RSA PRIVATE KEY-----\n"
14+
var privateKeyPrefix = "-----BEGIN RSA PRIVATE KEY-----"
15+
var privateKeySuffix = "-----END RSA PRIVATE KEY-----"
1616

1717
type GitHubError struct {
1818
Message string `json:"message"`
@@ -26,7 +26,7 @@ func CreateToken(id string, privateKey string) (string, error) {
2626
if !strings.HasPrefix(privateKey, privateKeyPrefix) {
2727
return "", fmt.Errorf("private-key should have prefix %s", privateKeyPrefix)
2828
}
29-
if !strings.HasSuffix(privateKey, privateKeySuffix) {
29+
if !strings.HasSuffix(strings.TrimSuffix(privateKey, "\n"), privateKeySuffix) {
3030
return "", fmt.Errorf("private-key should have suffix %s", privateKeySuffix)
3131
}
3232
dec, _ := pem.Decode([]byte(privateKey))
@@ -42,7 +42,7 @@ func CreateToken(id string, privateKey string) (string, error) {
4242
return token.SignedString(rsa)
4343
}
4444

45-
func CreateInstallationToken(installationId string, token string) (string, error) {
45+
func CreateInstallationToken(installId string, token string) (string, error) {
4646
gitHubInstallationToken := &GitHubInstallationToken{}
4747
gitHubError := &GitHubError{}
4848
res, err := client.R().
@@ -52,9 +52,9 @@ func CreateInstallationToken(installationId string, token string) (string, error
5252
SetResult(&gitHubInstallationToken).
5353
SetError(&gitHubError).
5454
SetPathParams(map[string]string{
55-
"installationId": installationId,
55+
"installId": installId,
5656
}).
57-
Post("https://api.github.com/app/installations/{installationId}/access_tokens")
57+
Post("https://api.github.com/app/installations/{installId}/access_tokens")
5858
if err != nil {
5959
return "", err
6060
}

main.go

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,39 +28,55 @@ func main() {
2828
Commands: []*cli.Command{
2929
{
3030
Name: "token",
31-
Usage: "Create a GitHub app installation token",
31+
Usage: "Create a GitHub App installation token",
3232
Aliases: []string{"t"},
3333
Flags: []cli.Flag{
3434
&cli.StringFlag{
3535
Name: "id",
36-
Usage: "GitHub app id",
37-
EnvVars: []string{"GITHUB_APP_ID"},
36+
Usage: "GitHub App id",
37+
EnvVars: []string{"GHAPP_ID", "GITHUB_APP_ID"},
3838
Required: true,
3939
},
4040
&cli.StringFlag{
41-
Name: "installation-id",
42-
Usage: "GitHub app installation-id",
43-
EnvVars: []string{"GITHUB_APP_INSTALLATION_ID"},
41+
Name: "install-id",
42+
Usage: "GitHub App installation id",
43+
EnvVars: []string{"GHAPP_INSTALL_ID", "GITHUB_APP_INSTALL_ID"},
4444
Required: true,
4545
},
4646
&cli.StringFlag{
47-
Name: "private-key",
48-
Usage: "GitHub app private-key",
49-
EnvVars: []string{"GITHUB_APP_PRIVATE_KEY"},
50-
FilePath: ".github-app-private-key.pem",
51-
DefaultText: ".github-app-private-key.pem",
52-
Required: true,
47+
Name: "private-key",
48+
Usage: "GitHub App private key",
49+
EnvVars: []string{"GHAPP_PRIVATE_KEY", "GITHUB_APP_PRIVATE_KEY"},
50+
},
51+
&cli.StringFlag{
52+
Name: "private-key-file",
53+
Usage: "GitHub App private key file like .ghapp-private-key.pem",
54+
EnvVars: []string{"GHAPP_PRIVATE_KEY_FILE", "GITHUB_APP_PRIVATE_KEY_FILE"},
5355
},
5456
},
5557
Action: func(ctx *cli.Context) error {
58+
if !ctx.IsSet("private-key") {
59+
if !ctx.IsSet("private-key-file") {
60+
return fmt.Errorf("private-key or private-key-file not set")
61+
}
62+
privateKeyFile := ctx.String("private-key-file")
63+
privateKeyBytes, err := os.ReadFile(privateKeyFile)
64+
if err != nil {
65+
return err
66+
}
67+
err = ctx.Set("private-key", string(privateKeyBytes))
68+
if err != nil {
69+
return err
70+
}
71+
}
5672
id := ctx.String("id")
57-
installationId := ctx.String("installation-id")
73+
installId := ctx.String("install-id")
5874
privateKey := ctx.String("private-key")
5975
token, err := cmd.CreateToken(id, privateKey)
6076
if err != nil {
6177
return err
6278
}
63-
installationToken, err := cmd.CreateInstallationToken(installationId, token)
79+
installationToken, err := cmd.CreateInstallationToken(installId, token)
6480
if err != nil {
6581
return err
6682
}
@@ -72,7 +88,8 @@ func main() {
7288
}
7389
sort.Sort(cli.FlagsByName(app.Flags))
7490
sort.Sort(cli.CommandsByName(app.Commands))
75-
if err := app.Run(os.Args); err != nil {
91+
err := app.Run(os.Args)
92+
if err != nil {
7693
fmt.Print(err)
7794
os.Exit(1)
7895
}

0 commit comments

Comments
 (0)