Skip to content

Commit 6cf67c8

Browse files
committed
feat(docker-build-images): allows to pass GitHub App token as secret
Signed-off-by: Emilien Escalle <[email protected]>
1 parent cb5160d commit 6cf67c8

File tree

7 files changed

+139
-22
lines changed

7 files changed

+139
-22
lines changed

.github/workflows/__main-ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ concurrency:
2424
jobs:
2525
ci:
2626
uses: ./.github/workflows/__shared-ci.yml
27+
secrets: inherit
2728

2829
clean:
2930
needs: ci

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ concurrency:
2222
jobs:
2323
ci:
2424
uses: ./.github/workflows/__shared-ci.yml
25+
secrets: inherit

.github/workflows/__shared-ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,4 @@ jobs:
5252
name: Test docker build images
5353
needs: linter
5454
uses: ./.github/workflows/__test-workflow-docker-build-images.yml
55+
secrets: inherit

.github/workflows/__test-workflow-docker-build-images.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ jobs:
2020
steps:
2121
- run: |
2222
if [ -z "${{ secrets.GITHUB_TOKEN }}" ]; then
23-
echo "GitHub token secrets is not set"
23+
echo "GitHub token secret is not set"
2424
exit 1
2525
fi
26+
2627
act:
2728
needs: arrange
2829
uses: ./.github/workflows/docker-build-images.yml
@@ -31,6 +32,7 @@ jobs:
3132
build-secrets: |
3233
SECRET_REPOSITORY_OWNER=${{ github.repository_owner }}
3334
SECRET_REPOSITORY=${{ github.repository }}
35+
build-secret-github-app-key: ${{ secrets.CI_BOT_APP_PRIVATE_KEY }}
3436
with:
3537
# First image is multi arch
3638
# Second image is mono arch
@@ -71,6 +73,10 @@ jobs:
7173
}
7274
}
7375
]
76+
build-secret-github-app-id: ${{ vars.CI_BOT_APP_ID }}
77+
build-secret-github-app-token-env: |
78+
SECRET_ENV_GITHUB_APP_TOKEN_1
79+
SECRET_ENV_GITHUB_APP_TOKEN_2
7480
7581
assert:
7682
needs: act

.github/workflows/docker-build-images.md

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ jobs:
5050
# See https://github.com/docker/login-action#usage.
5151
oci-registry-password: ${{ secrets.GITHUB_TOKEN }}
5252

53+
# List of secrets to expose to the build.
54+
# See <https://docs.docker.com/build/ci/github-actions/secrets/>.
55+
build-secrets: ""
56+
57+
# GitHub App private key to generate GitHub token to be passed as build secret env.
58+
# See <https://github.com/actions/create-github-app-token>.
59+
build-secret-github-app-key: ""
60+
5361
# Optional customizations.
5462
with:
5563
# Json array of runner(s) to use.
@@ -88,32 +96,54 @@ jobs:
8896
# }
8997
# ]
9098
images: ""
99+
100+
# Enable Git LFS.
101+
# See <https://github.com/actions/checkout?tab=readme-ov-file#usage>.
102+
# Default: true
103+
lfs: true
104+
105+
# Environment variable name(s) to pass GitHub token generated by GitHub App.
106+
# Can be a multiline string list.
107+
# This is useful to pass a generated token to the build, as it is not possible to share generated secrets between jobs.
108+
# Needs input `build-secret-github-app-id` and secret `build-secret-github-app-key`.
109+
# Default: "GITHUB_APP_TOKEN"
110+
build-secret-github-app-token-env: |
111+
GITHUB_APP_TOKEN
112+
113+
# GitHub App ID to generate GitHub token to be passed as build secret env.
114+
# See <https://github.com/actions/create-github-app-token>.
115+
build-secret-github-app-id: ""
91116
```
92117
93118
<!-- end usage -->
94119
<!-- start secrets -->
95120
96121
## Secrets
97122
98-
| **Secret** | **Description** |
99-
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
100-
| **<code>oci-registry-password</code>** | Password or GitHub token (`packages:read` and `packages:write` scopes) used to log against the OCI registry. See <https://github.com/docker/login-action#usage>. |
123+
| **Secret** | **Description** | **Required** |
124+
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
125+
| **<code>oci-registry-password</code>** | Password or GitHub token (`packages:read` and `packages:write` scopes) used to log against the OCI registry. See <https://github.com/docker/login-action#usage>. | **true** |
126+
| **<code>build-secrets</code>** | List of secrets to expose to the build. See <https://docs.docker.com/build/ci/github-actions/secrets/>. | **false** |
127+
| **<code>build-secret-github-app-key</code>** | GitHub App private key to generate GitHub token to be passed as build secret env. See <https://github.com/actions/create-github-app-token>. | **false** |
101128

102129
<!-- end secrets -->
130+
<!-- markdownlint-disable MD013 -->
103131
<!-- start inputs -->
104132

105133
## Inputs
106134

107-
| **Input** | **Description** | **Default** | **Required** |
108-
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------------ |
109-
| **<code>runs-on</code>** | Json array of runner(s) to use. See <https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job> | <code>["ubuntu-latest"]</code> | **false** |
110-
| **<code>oci-registry</code>** | OCI registry where to pull and push images | <code>ghcr.io</code> | **false** |
111-
| **<code>oci-registry-username</code>** | Username used to log against the OCI registry. See <https://github.com/docker/login-action#usage> | <code>${{ github.repository_owner }}</code> | **false** |
112-
| **<code>images</code>** | Images to build parameters. | | **true** |
113-
| | Example: <code>[{ "name": "application", "context": ".", "dockerfile": "./docker/application/Dockerfile", "target": "prod", "build-args": { "APP_PATH": "./application/", "PROD_MODE": "true" }, "platforms": ["linux/amd64", { "name": "darwin/amd64", "runs-on": "macos-latest" }] }]</code> | | |
114-
| **<code>lfs</code>** | Enable Git LFS. See <https://github.com/actions/checkout?tab=readme-ov-file#usage>. | <code>true</code> | **false** |
135+
| **Input** | **Description** | **Default** | **Required** | **Type** |
136+
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------------ | ----------- |
137+
| **<code>runs-on</code>** | Json array of runner(s) to use. See <https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job> | <code>["ubuntu-latest"]</code> | **false** | **string** |
138+
| **<code>oci-registry</code>** | OCI registry where to pull and push images | <code>ghcr.io</code> | **false** | **string** |
139+
| **<code>oci-registry-username</code>** | Username used to log against the OCI registry. See <https://github.com/docker/login-action#usage> | <code>${{ github.repository_owner }}</code> | **false** | **string** |
140+
| **<code>images</code>** | Images to build parameters. Json array of objects. Example: [{ "name": "application", "context": ".", "dockerfile": "./docker/application/Dockerfile", "target": "prod", "build-args": { "APP_PATH": "./application/", "PROD_MODE": "true" }, "secret-envs": { "GH_TOKEN": "GITHUB_TOKEN" }, "platforms": ["linux/amd64", { "name": "darwin/amd64", "runs-on": "macos-latest" }] }] | | **true** | **string** |
141+
| **<code>lfs</code>** | Enable Git LFS. See <https://github.com/actions/checkout?tab=readme-ov-file#usage>. | <code>true</code> | **false** | **boolean** |
142+
| **<code>build-secret-github-app-token-env</code>** | Environment variable name(s) to pass GitHub token generated by GitHub App. Can be a multiline string list. This is useful to pass a generated token to the build, as it is not possible to share generated secrets between jobs. Needs input `build-secret-github-app-id` and secret `build-secret-github-app-key`. | <code>GITHUB_APP_TOKEN</code> | **false** | **string** |
143+
| **<code>build-secret-github-app-id</code>** | GitHub App ID to generate GitHub token to be passed as build secret env. See <https://github.com/actions/create-github-app-token>. | | **false** | **string** |
115144

116145
<!-- end inputs -->
146+
<!-- markdownlint-enable MD013 -->
117147

118148
### Images entry parameters
119149

.github/workflows/docker-build-images.yml

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ on: # yamllint disable-line rule:truthy
8686
type: boolean
8787
default: true
8888
required: false
89+
build-secret-github-app-token-env:
90+
description: |
91+
Environment variable name(s) to pass GitHub token generated by GitHub App.
92+
Can be a multiline string list.
93+
This is useful to pass a generated token to the build, as it is not possible to share generated secrets between jobs.
94+
Needs input `build-secret-github-app-id` and secret `build-secret-github-app-key`.
95+
type: string
96+
required: false
97+
default: "GITHUB_APP_TOKEN"
98+
build-secret-github-app-id:
99+
description: |
100+
GitHub App ID to generate GitHub token to be passed as build secret env.
101+
See <https://github.com/actions/create-github-app-token>.
102+
required: false
103+
type: string
89104
secrets:
90105
oci-registry-password:
91106
description: |
@@ -97,6 +112,11 @@ on: # yamllint disable-line rule:truthy
97112
List of secrets to expose to the build.
98113
See <https://docs.docker.com/build/ci/github-actions/secrets/>.
99114
required: false
115+
build-secret-github-app-key:
116+
description: |
117+
GitHub App private key to generate GitHub token to be passed as build secret env.
118+
See <https://github.com/actions/create-github-app-token>.
119+
required: false
100120

101121
permissions:
102122
contents: read
@@ -322,6 +342,43 @@ jobs:
322342
echo "self-workflow" >> .gitignore
323343
echo "self-workflow" >> .dockerignore
324344
345+
- uses: actions/create-github-app-token@v1
346+
if: inputs.build-secret-github-app-id
347+
id: generate-token
348+
with:
349+
app-id: ${{ inputs.build-secret-github-app-id }}
350+
private-key: ${{ secrets.build-secret-github-app-key }}
351+
352+
- id: prepare-secret-envs
353+
uses: actions/[email protected]
354+
with:
355+
script: |
356+
const githubAppTokenEnvName = 'GITHUB_APP_TOKEN';
357+
358+
let secretEnvs = `${{ matrix.image.secret-envs }}`;
359+
const githubAppToken = `${{ steps.generate-token.outputs.token }}`;
360+
361+
if (!githubAppToken) {
362+
if (secretEnvs) {
363+
core.setOutput('secret-envs', secretEnvs);
364+
}
365+
return;
366+
}
367+
368+
core.exportVariable(githubAppTokenEnvName, githubAppToken);
369+
370+
const buildSecretGithubAppTokenEnv = `${{ inputs.build-secret-github-app-token-env }}`;
371+
if (!buildSecretGithubAppTokenEnv) {
372+
throw new Error(`"build-secret-github-app-token-env" input is missing`);
373+
}
374+
375+
secretEnvs += '\n' + buildSecretGithubAppTokenEnv
376+
.split('\n')
377+
.filter(Boolean)
378+
.map(key => `${key}=${githubAppTokenEnvName}`).join('\n');
379+
380+
core.setOutput('secret-envs', secretEnvs);
381+
325382
- id: build
326383
uses: ./self-workflow/actions/docker/build-image
327384
with:
@@ -336,7 +393,7 @@ jobs:
336393
build-args: ${{ matrix.image.build-args }}
337394
target: ${{ matrix.image.target }}
338395
platform: ${{ matrix.image.platform }}
339-
secret-envs: ${{ matrix.image.secret-envs }}
396+
secret-envs: ${{ steps.prepare-secret-envs.outputs.secret-envs }}
340397
secrets: ${{ secrets.build-secrets }}
341398

342399
# FIXME: Set built images infos in file to be uploaded as artifacts, because github action does not handle job outputs for matrix

tests/application/Dockerfile

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,39 @@ ARG BUILD_REPOSITORY
2626
RUN test -n "$BUILD_REPOSITORY" || (echo "Error: BUILD_REPOSITORY is not set" && exit 1);
2727
RUN test "$BUILD_REPOSITORY" = "$EXPECTED_REPOSITORY" || (echo "Error: BUILD_REPOSITORY is not \"$EXPECTED_REPOSITORY\"" && exit 1);
2828

29-
# Test that secrets are passed
30-
RUN --mount=type=secret,id=SECRET_REPOSITORY_OWNER test -f /run/secrets/SECRET_REPOSITORY_OWNER || (echo "Error: SECRET_REPOSITORY_OWNER is not set" && exit 1);
31-
RUN --mount=type=secret,id=SECRET_REPOSITORY_OWNER test "$(cat /run/secrets/SECRET_REPOSITORY_OWNER)" = "$EXPECTED_REPOSITORY_OWNER" || (echo "Error: SECRET_REPOSITORY_OWNER is not \"$EXPECTED_REPOSITORY_OWNER\"" && exit 1);
29+
RUN cat >test.sh <<EOF
30+
31+
assertSecretExists() {
32+
local secretName="\$1"
33+
test -f "/run/secrets/\$secretName" || {
34+
echo "Error: \$secretName is not set"
35+
exit 1
36+
}
37+
}
38+
39+
assertSecretEquals() {
40+
local secretName="\$1"
41+
local expectedValue="\$2"
3242

33-
RUN --mount=type=secret,id=SECRET_REPOSITORY test -f /run/secrets/SECRET_REPOSITORY || (echo "Error: SECRET_REPOSITORY is not set" && exit 1);
34-
RUN --mount=type=secret,id=SECRET_REPOSITORY test "$(cat /run/secrets/SECRET_REPOSITORY)" = "$EXPECTED_REPOSITORY" || (echo "Error: SECRET_REPOSITORY is not \"$EXPECTED_REPOSITORY\"" && exit 1);
43+
assertSecretExists "\$secretName"
44+
45+
test "\$(cat /run/secrets/"\$secretName")" = "\$expectedValue" || {
46+
echo "Error: \$secretName is not \"\$expectedValue\""
47+
exit 1
48+
}
49+
}
50+
EOF
51+
52+
# Test that secrets are passed
53+
RUN --mount=type=secret,id=SECRET_REPOSITORY_OWNER . test.sh && assertSecretEquals SECRET_REPOSITORY_OWNER "$EXPECTED_REPOSITORY_OWNER";
54+
RUN --mount=type=secret,id=SECRET_REPOSITORY . test.sh && assertSecretEquals SECRET_REPOSITORY "$EXPECTED_REPOSITORY";
3555

3656
# Test that secret envs are passed
37-
RUN --mount=type=secret,id=SECRET_ENV_REPOSITORY_OWNER test -f /run/secrets/SECRET_ENV_REPOSITORY_OWNER || (echo "Error: SECRET_ENV_REPOSITORY_OWNER is not set" && exit 1);
38-
RUN --mount=type=secret,id=SECRET_ENV_REPOSITORY_OWNER test "$(cat /run/secrets/SECRET_ENV_REPOSITORY_OWNER)" = "$EXPECTED_REPOSITORY_OWNER" || (echo "Error: SECRET_ENV_REPOSITORY_OWNER is not \"$EXPECTED_REPOSITORY_OWNER\"" && exit 1);
57+
RUN --mount=type=secret,id=SECRET_ENV_REPOSITORY_OWNER . test.sh && assertSecretEquals SECRET_ENV_REPOSITORY_OWNER "$EXPECTED_REPOSITORY_OWNER";
58+
RUN --mount=type=secret,id=SECRET_ENV_REPOSITORY . test.sh && assertSecretEquals SECRET_ENV_REPOSITORY "$EXPECTED_REPOSITORY";
3959

40-
RUN --mount=type=secret,id=SECRET_ENV_REPOSITORY test -f /run/secrets/SECRET_ENV_REPOSITORY || (echo "Error: SECRET_ENV_REPOSITORY is not set" && exit 1);
41-
RUN --mount=type=secret,id=SECRET_ENV_REPOSITORY test "$(cat /run/secrets/SECRET_ENV_REPOSITORY)" = "$EXPECTED_REPOSITORY" || (echo "Error: SECRET_ENV_REPOSITORY is not \"$EXPECTED_REPOSITORY\"" && exit 1);
60+
# Test that the github app token secrets are passed
61+
RUN --mount=type=secret,id=SECRET_ENV_GITHUB_APP_TOKEN_1 . test.sh && assertSecretExists SECRET_ENV_GITHUB_APP_TOKEN_1;
62+
RUN --mount=type=secret,id=SECRET_ENV_GITHUB_APP_TOKEN_2 . test.sh && assertSecretExists SECRET_ENV_GITHUB_APP_TOKEN_2;
4263

4364
USER test

0 commit comments

Comments
 (0)