Compare commits
43 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2174804b |
||
|
|
0dfce0c0f8 |
||
|
|
7c6bc770da |
||
|
|
42e4997ee3 |
||
|
|
5ea4dc1147 |
||
|
|
c200f3691d |
||
|
|
3a53be7e7c |
||
|
|
b7593ed2ef |
||
|
|
c1ee334b4f |
||
|
|
140bb5113f |
||
|
|
ab3132e2ad |
||
|
|
25b96bac99 |
||
|
|
0920706a19 |
||
|
|
ba79af0395 |
||
|
|
bfaa66bd66 |
||
|
|
d0822ad9bf |
||
|
|
7b53cdc2a3 |
||
|
|
a9cfddf5d2 |
||
|
|
b011f3988e |
||
|
|
71f986410d |
||
|
|
0cd8f2e4e2 |
||
|
|
332e0ba72f |
||
|
|
28d44ba259 |
||
|
|
83354cacbb |
||
|
|
6fc4af4b14 |
||
|
|
212f83afe8 |
||
|
|
8254fb75a3 |
||
|
|
d1b27fe5c4 |
||
|
|
c8788cc4c5 |
||
|
|
62cf5bd3e4 |
||
|
|
0a94a84ba5 |
||
|
|
699582eeaf |
||
|
|
6384b341b7 |
||
|
|
f112390a2d |
||
|
|
984b9cfee9 |
||
|
|
5e210ff4ed |
||
|
|
49ae1e804e |
||
|
|
71fee32a0b |
||
|
|
e0122d6a97 |
||
|
|
34baaec3f3 |
||
|
|
8d44d59719 |
||
|
|
d176447fc7 |
||
|
|
33e827c6cc |
27 changed files with 1102 additions and 943 deletions
15
.eslintrc.js
15
.eslintrc.js
|
|
@ -1,15 +0,0 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
||||
5
.github/actionlint.yml
vendored
Normal file
5
.github/actionlint.yml
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
paths:
|
||||
'**/*.yml':
|
||||
ignore:
|
||||
# https://github.com/rhysd/actionlint/issues/559
|
||||
- 'invalid runner name "node24"'
|
||||
21
.github/workflows/draft-release.yml
vendored
21
.github/workflows/draft-release.yml
vendored
|
|
@ -1,17 +1,3 @@
|
|||
# Copyright 2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: 'Draft release'
|
||||
|
||||
on:
|
||||
|
|
@ -29,10 +15,11 @@ on:
|
|||
|
||||
jobs:
|
||||
draft-release:
|
||||
name: 'Draft release'
|
||||
uses: 'google-github-actions/.github/.github/workflows/draft-release.yml@v0'
|
||||
uses: 'google-github-actions/.github/.github/workflows/draft-release.yml@v3' # ratchet:exclude
|
||||
permissions:
|
||||
contents: 'read'
|
||||
pull-requests: 'write'
|
||||
with:
|
||||
version_strategy: '${{ github.event.inputs.version_strategy }}'
|
||||
# secrets must be explicitly passed to reusable workflows https://docs.github.com/en/enterprise-cloud@latest/actions/using-workflows/reusing-workflows#using-inputs-and-secrets-in-a-reusable-workflow
|
||||
secrets:
|
||||
ACTIONS_BOT_TOKEN: '${{ secrets.ACTIONS_BOT_TOKEN }}'
|
||||
|
|
|
|||
25
.github/workflows/publish.yml
vendored
Normal file
25
.github/workflows/publish.yml
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
name: 'Publish immutable action version'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types:
|
||||
- 'published'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
contents: 'read'
|
||||
id-token: 'write'
|
||||
packages: 'write'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
|
||||
|
||||
- name: 'Publish'
|
||||
id: 'publish'
|
||||
uses: 'actions/publish-immutable-action@4bc8754ffc40f27910afb20287dbbbb675a4e978' # ratchet:actions/publish-immutable-action@v0.0.4
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
|
|
@ -1,17 +1,3 @@
|
|||
# Copyright 2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: 'Release'
|
||||
|
||||
on:
|
||||
|
|
@ -22,6 +8,10 @@ on:
|
|||
|
||||
jobs:
|
||||
release:
|
||||
if: "startsWith(github.event.head_commit.message, 'Release: v')"
|
||||
name: 'Release'
|
||||
uses: 'google-github-actions/.github/.github/workflows/release.yml@v0'
|
||||
uses: 'google-github-actions/.github/.github/workflows/release.yml@v3' # ratchet:exclude
|
||||
permissions:
|
||||
attestations: 'write'
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
secrets:
|
||||
ACTIONS_BOT_TOKEN: '${{ secrets.ACTIONS_BOT_TOKEN }}'
|
||||
|
|
|
|||
57
.github/workflows/test.yml
vendored
57
.github/workflows/test.yml
vendored
|
|
@ -29,6 +29,10 @@ concurrency:
|
|||
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: 'read'
|
||||
statuses: 'write'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
|
@ -39,18 +43,15 @@ jobs:
|
|||
runs-on: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
- uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
|
||||
|
||||
- uses: 'actions/setup-node@v4'
|
||||
- uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: 'npm build'
|
||||
run: 'npm ci && npm run build'
|
||||
|
||||
- name: 'npm lint'
|
||||
run: 'npm run lint'
|
||||
|
||||
- name: 'npm test'
|
||||
run: 'npm run test'
|
||||
|
||||
|
|
@ -59,7 +60,8 @@ jobs:
|
|||
# Direct Workload Identity Federation
|
||||
#
|
||||
direct_workload_identity_federation:
|
||||
if: ${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
if: |-
|
||||
${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
name: 'direct_workload_identity_federation'
|
||||
runs-on: '${{ matrix.os }}'
|
||||
strategy:
|
||||
|
|
@ -74,11 +76,11 @@ jobs:
|
|||
id-token: 'write'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
- uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
|
||||
|
||||
- uses: 'actions/setup-node@v4'
|
||||
- uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: 'npm build'
|
||||
run: 'npm ci && npm run build'
|
||||
|
|
@ -99,7 +101,7 @@ jobs:
|
|||
--fail \
|
||||
--header "Authorization: Bearer ${{ steps.auth-default.outputs.auth_token }}"
|
||||
|
||||
- uses: 'google-github-actions/setup-gcloud@v2'
|
||||
- uses: 'google-github-actions/setup-gcloud@main' # ratchet:exclude
|
||||
with:
|
||||
version: '>= 363.0.0'
|
||||
|
||||
|
|
@ -112,7 +114,8 @@ jobs:
|
|||
# Workload Identity Federation through a Service Account
|
||||
#
|
||||
workload_identity_federation_through_service_account:
|
||||
if: ${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
if: |-
|
||||
${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
name: 'workload_identity_federation_through_service_account'
|
||||
runs-on: '${{ matrix.os }}'
|
||||
strategy:
|
||||
|
|
@ -127,11 +130,11 @@ jobs:
|
|||
id-token: 'write'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
- uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
|
||||
|
||||
- uses: 'actions/setup-node@v4'
|
||||
- uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: 'npm build'
|
||||
run: 'npm ci && npm run build'
|
||||
|
|
@ -143,7 +146,7 @@ jobs:
|
|||
workload_identity_provider: '${{ vars.WIF_PROVIDER_NAME }}'
|
||||
service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
|
||||
|
||||
- uses: 'google-github-actions/setup-gcloud@v2'
|
||||
- uses: 'google-github-actions/setup-gcloud@main' # ratchet:exclude
|
||||
with:
|
||||
version: '>= 363.0.0'
|
||||
|
||||
|
|
@ -183,7 +186,8 @@ jobs:
|
|||
# Service Account Key JSON
|
||||
#
|
||||
credentials_json:
|
||||
if: ${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
if: |-
|
||||
${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
name: 'credentials_json'
|
||||
runs-on: '${{ matrix.os }}'
|
||||
strategy:
|
||||
|
|
@ -195,11 +199,11 @@ jobs:
|
|||
- 'macos-latest'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
- uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
|
||||
|
||||
- uses: 'actions/setup-node@v4'
|
||||
- uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: 'npm build'
|
||||
run: 'npm ci && npm run build'
|
||||
|
|
@ -210,7 +214,7 @@ jobs:
|
|||
with:
|
||||
credentials_json: '${{ secrets.SERVICE_ACCOUNT_KEY_JSON }}'
|
||||
|
||||
- uses: 'google-github-actions/setup-gcloud@v2'
|
||||
- uses: 'google-github-actions/setup-gcloud@main' # ratchet:exclude
|
||||
with:
|
||||
version: '>= 363.0.0'
|
||||
|
||||
|
|
@ -250,17 +254,18 @@ jobs:
|
|||
# has permissions to read the file.
|
||||
#
|
||||
docker:
|
||||
if: ${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
if: |-
|
||||
${{ github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name }}
|
||||
name: 'docker'
|
||||
runs-on: 'ubuntu-latest'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
- uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
|
||||
|
||||
- uses: 'actions/setup-node@v4'
|
||||
- uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: 'npm build'
|
||||
run: 'npm ci && npm run build'
|
||||
|
|
@ -271,7 +276,7 @@ jobs:
|
|||
credentials_json: '${{ secrets.SERVICE_ACCOUNT_KEY_JSON }}'
|
||||
|
||||
- name: 'docker'
|
||||
uses: 'docker://alpine:3'
|
||||
uses: 'docker://index.docker.io/library/alpine@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1' # ratchet:docker://alpine:3
|
||||
with:
|
||||
entrypoint: '/bin/sh'
|
||||
args: '-euc "test -n "${GOOGLE_APPLICATION_CREDENTIALS}" && test -r "${GOOGLE_APPLICATION_CREDENTIALS}"'
|
||||
|
|
|
|||
2
.github/workflows/troubleshooting.yml
vendored
2
.github/workflows/troubleshooting.yml
vendored
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
runs-on: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/github-script@v6'
|
||||
- uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # ratchet:actions/github-script@v7
|
||||
with:
|
||||
script: |-
|
||||
const msg =
|
||||
|
|
|
|||
52
README.md
52
README.md
|
|
@ -14,6 +14,10 @@ Action to authenticate to Google Cloud:
|
|||
1. [Workload Identity Federation through a Service Account](#indirect-wif)
|
||||
1. [Service Account Key JSON](#sake)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The `gsutil` command will **not** use the credentials exported by this GitHub
|
||||
> Action. Customers should use `gcloud storage` instead.
|
||||
|
||||
**This is not an officially supported Google product, and it is not covered by a
|
||||
Google Cloud support contract. To report bugs or request features in a Google
|
||||
Cloud product, please contact [Google Cloud
|
||||
|
|
@ -35,10 +39,7 @@ support](https://cloud.google.com/support).**
|
|||
gha-creds-*.json
|
||||
```
|
||||
|
||||
- To use the `bq` or `gsutil` tools, use the Google Cloud SDK version 390.0.0
|
||||
or newer.
|
||||
|
||||
- This action runs using Node 20. Use a [runner
|
||||
- This action runs using Node 24. Use a [runner
|
||||
version](https://github.com/actions/virtual-environments) that supports this
|
||||
version of Node or newer.
|
||||
|
||||
|
|
@ -48,6 +49,9 @@ support](https://cloud.google.com/support).**
|
|||
```yaml
|
||||
jobs:
|
||||
job_id:
|
||||
# Any runner supporting Node 20 or newer
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Add "id-token" with the intended permissions.
|
||||
permissions:
|
||||
contents: 'read'
|
||||
|
|
@ -56,7 +60,7 @@ jobs:
|
|||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
project_id: 'my-project'
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
|
|
@ -80,6 +84,12 @@ For more usage options, see the [examples](docs/EXAMPLES.md).
|
|||
> SDK](https://github.com/firebase/firebase-admin-node/issues/1377). Use Service
|
||||
> Account Key JSON authentication instead.
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> As of the time of this writing, the GitHub OIDC token expires in 5 minutes,
|
||||
> which means any derived credentials also expire in 5 minutes.
|
||||
|
||||
|
||||
The following inputs are for _authenticating_ to Google Cloud via Workload
|
||||
Identity Federation.
|
||||
|
||||
|
|
@ -191,6 +201,10 @@ Cloud as an output for use in future steps in the workflow. These options only
|
|||
apply to ID tokens generated by this action. By default, this action does not
|
||||
generate any tokens.
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> ID Tokens have a maximum lifetime of 10 minutes. This value cannot be changed.
|
||||
|
||||
- `service_account`: (Required) Email address or unique identifier of the
|
||||
Google Cloud service account for which to generate the ID token. For
|
||||
example:
|
||||
|
|
@ -235,7 +249,7 @@ regardless of the authentication mechanism.
|
|||
job_id:
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4' # Must come first!
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
```
|
||||
|
||||
- `export_environment_variables`: (Optional) If true, the action will export
|
||||
|
|
@ -269,13 +283,22 @@ regardless of the authentication mechanism.
|
|||
https://cloud.google.com. Trusted Partner Cloud and Google Distributed
|
||||
Hosted Cloud should set this to their universe address.
|
||||
|
||||
You can also override individual API endpoints by setting the environment variable `GHA_ENDPOINT_OVERRIDE_<endpoint>` where endpoint is the API endpoint to override. This only applies to the `auth` action and does not persist to other steps. For example:
|
||||
You can also override individual API endpoints by setting the environment
|
||||
variable `GHA_ENDPOINT_OVERRIDE_<endpoint>` where endpoint is the API
|
||||
endpoint to override. This only applies to the `auth` action and does not
|
||||
persist to other steps. For example:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
GHA_ENDPOINT_OVERRIDE_oauth2: 'https://oauth2.myapi.endpoint/v1'
|
||||
```
|
||||
|
||||
- `request_reason`: (Optional) An optional Reason Request [System
|
||||
Parameter](https://cloud.google.com/apis/docs/system-parameters) for each
|
||||
API call made by the GitHub Action. This will inject the
|
||||
"X-Goog-Request-Reason" HTTP header, which will provide user-supplied
|
||||
information in Google Cloud audit logs.
|
||||
|
||||
- `cleanup_credentials`: (Optional) If true, the action will remove any
|
||||
created credentials from the filesystem upon completion. This only applies
|
||||
if "create_credentials_file" is true. The default is true.
|
||||
|
|
@ -299,7 +322,6 @@ regardless of the authentication mechanism.
|
|||
"token_format" is "id_token".
|
||||
|
||||
|
||||
|
||||
<a id="setup"></a>
|
||||
## Setup
|
||||
|
||||
|
|
@ -324,8 +346,8 @@ In this setup, the Workload Identity Pool has direct IAM permissions on Google
|
|||
Cloud resources; there are no intermediate service accounts or keys. This is
|
||||
preferred since it directly authenticates GitHub Actions to Google Cloud without
|
||||
a proxy resource. However, not all Google Cloud resources support `principalSet`
|
||||
identities. Please see the documentation for your Google Cloud service for more
|
||||
information.
|
||||
identities, and the resulting token has a maximum lifetime of 10 minutes. Please
|
||||
see the documentation for your Google Cloud service for more information.
|
||||
|
||||
[](docs/google-github-actions-auth-direct-workload-identity-federation.svg)
|
||||
|
||||
|
|
@ -412,7 +434,7 @@ These instructions use the [gcloud][gcloud] command-line tool.
|
|||
Actions YAML:
|
||||
|
||||
```yaml
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
project_id: 'my-project'
|
||||
workload_identity_provider: '...' # "projects/123456789/locations/global/workloadIdentityPools/github/providers/my-repo"
|
||||
|
|
@ -576,7 +598,7 @@ These instructions use the [gcloud][gcloud] command-line tool.
|
|||
Actions YAML:
|
||||
|
||||
```yaml
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
service_account: '...' # my-service-account@my-project.iam.gserviceaccount.com
|
||||
workload_identity_provider: '...' # "projects/123456789/locations/global/workloadIdentityPools/github/providers/my-repo"
|
||||
|
|
@ -609,7 +631,7 @@ as a secret.
|
|||
> [!CAUTION]
|
||||
>
|
||||
> Google Cloud Service Account Key JSON files must be secured
|
||||
> and treated like a password. Anyone with acess to the JSON key can
|
||||
> and treated like a password. Anyone with access to the JSON key can
|
||||
> authenticate to Google Cloud as the underlying Service Account. By default,
|
||||
> these credentials never expire, which is why the former authentication options
|
||||
> are much preferred.
|
||||
|
|
@ -641,11 +663,11 @@ These instructions use the [gcloud][gcloud] command-line tool.
|
|||
1. Upload the contents of this file as a [GitHub Actions
|
||||
Secret](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions).
|
||||
|
||||
Use the name of the GitHub Actios secret as the `credentials_json` value in
|
||||
Use the name of the GitHub Actions secret as the `credentials_json` value in
|
||||
the GitHub Actions YAML:
|
||||
|
||||
```yaml
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CREDENTIALS }}' # Replace with the name of your GitHub Actions secret
|
||||
```
|
||||
|
|
|
|||
40
action.yml
40
action.yml
|
|
@ -56,7 +56,7 @@ inputs:
|
|||
description: |-
|
||||
If true, the action will securely generate a credentials file which can be
|
||||
used for authentication via gcloud and Google Cloud SDKs.
|
||||
default: true
|
||||
default: 'true'
|
||||
required: false
|
||||
export_environment_variables:
|
||||
description: |-
|
||||
|
|
@ -79,7 +79,7 @@ inputs:
|
|||
If false, the action will not export any environment variables, meaning
|
||||
future steps are unlikely to be automatically authenticated to Google
|
||||
Cloud.
|
||||
default: true
|
||||
default: 'true'
|
||||
required: false
|
||||
token_format:
|
||||
description: |-
|
||||
|
|
@ -102,12 +102,18 @@ inputs:
|
|||
Hosted Cloud should set this to their universe address.
|
||||
required: false
|
||||
default: 'googleapis.com'
|
||||
request_reason:
|
||||
description: |-
|
||||
An optional Reason Request System Parameter for each API call made by the
|
||||
GitHub Action. This will inject the "X-Goog-Request-Reason" HTTP header,
|
||||
which will provide user-supplied information in Google Cloud audit logs.
|
||||
required: false
|
||||
cleanup_credentials:
|
||||
description: |-
|
||||
If true, the action will remove any created credentials from the
|
||||
filesystem upon completion. This only applies if "create_credentials_file"
|
||||
is true.
|
||||
default: true
|
||||
default: 'true'
|
||||
required: false
|
||||
|
||||
# access token params
|
||||
|
|
@ -132,30 +138,6 @@ inputs:
|
|||
default: ''
|
||||
required: false
|
||||
|
||||
# retries - TODO - remove in v3.0
|
||||
retries:
|
||||
description: |-
|
||||
Number of times to retry a failed authentication attempt. This is useful
|
||||
for automated pipelines that may execute before IAM permissions are fully
|
||||
propogated.
|
||||
deprecationMessage: |-
|
||||
This field is no longer used and will be removed in a future release.
|
||||
required: false
|
||||
backoff:
|
||||
description: |-
|
||||
Delay time before trying another authentication attempt. This is
|
||||
implemented using a fibonacci backoff method (e.g. 1-1-2-3-5). The default
|
||||
value is 250 milliseconds.
|
||||
deprecationMessage: |-
|
||||
This field is no longer used and will be removed in a future release.
|
||||
required: false
|
||||
backoff_limit:
|
||||
description: |-
|
||||
Limits the retry backoff to the specified value.
|
||||
deprecationMessage: |-
|
||||
This field is no longer used and will be removed in a future release.
|
||||
required: false
|
||||
|
||||
# id token params
|
||||
id_token_audience:
|
||||
description: |-
|
||||
|
|
@ -169,7 +151,7 @@ inputs:
|
|||
generated token. If true, the token will contain "email" and
|
||||
"email_verified" claims. This is only valid when "token_format" is
|
||||
"id_token".
|
||||
default: false
|
||||
default: 'false'
|
||||
required: false
|
||||
|
||||
outputs:
|
||||
|
|
@ -198,6 +180,6 @@ branding:
|
|||
color: 'blue'
|
||||
|
||||
runs:
|
||||
using: 'node20'
|
||||
using: 'node24'
|
||||
main: 'dist/main/index.js'
|
||||
post: 'dist/post/index.js'
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eEuo pipefail
|
||||
|
||||
#
|
||||
# As of Node 20, the --test parameter does not support globbing, and it does not
|
||||
# support variable Windows paths. We also cannot invoke the test runner
|
||||
# directly, because while it has an API, there's no way to force it to transpile
|
||||
# the Typescript into JavaScript before passing it to the runner.
|
||||
#
|
||||
# So we're left with this solution, which shells out to Node to list all files
|
||||
# that end in *.test.ts (excluding node_modules/), and then execs out to that
|
||||
# process. We have to exec so the stderr/stdout and exit code is appropriately
|
||||
# fed to the caller.
|
||||
#
|
||||
|
||||
FILES="$(node -e "process.stdout.write(require('node:fs').readdirSync('./', { recursive: true }).filter((e) => {return e.endsWith('.test.ts') && !e.startsWith('node_modules');}).sort().join(' '));")"
|
||||
|
||||
set -x
|
||||
exec node --require ts-node/register --test-reporter spec --test ${FILES}
|
||||
6
dist/main/index.js
vendored
6
dist/main/index.js
vendored
File diff suppressed because one or more lines are too long
6
dist/post/index.js
vendored
6
dist/post/index.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
id-token: 'write'
|
||||
|
||||
- id: 'auth'
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
project_id: 'my-project'
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
contents: 'read'
|
||||
id-token: 'write'
|
||||
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
project_id: 'my-project'
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
|
|
@ -56,7 +56,7 @@ jobs:
|
|||
# the service account, specify the 'token_format' parameter and use the
|
||||
# 'accesss_token' output.
|
||||
#
|
||||
# - uses: 'google-github-actions/auth@v2'
|
||||
# - uses: 'google-github-actions/auth@v3'
|
||||
# with:
|
||||
# workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
# service_account: 'my-service-account@my-project.iam.gserviceaccount.com'
|
||||
|
|
@ -79,7 +79,7 @@ jobs:
|
|||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CREDENTIALS }}'
|
||||
```
|
||||
|
|
@ -100,7 +100,7 @@ jobs:
|
|||
- uses: 'actions/checkout@v4'
|
||||
|
||||
- id: 'auth'
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
project_id: 'my-project'
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
|
|
@ -136,7 +136,7 @@ jobs:
|
|||
- uses: 'actions/checkout@v4'
|
||||
|
||||
- id: 'auth'
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
token_format: 'access_token' # <--
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
|
|
@ -173,7 +173,7 @@ jobs:
|
|||
- uses: 'actions/checkout@v4'
|
||||
|
||||
- id: 'auth'
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
token_format: 'id_token' # <--
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
|
|
@ -187,6 +187,69 @@ jobs:
|
|||
run: |-
|
||||
curl https://myapp-uvehjacqzq.a.run.app \
|
||||
--header "Authorization: Bearer ${{ steps.auth.outputs.id_token }}"
|
||||
|
||||
# Example of using ID token in Python code
|
||||
- id: 'python-example'
|
||||
run: |-
|
||||
python -c "
|
||||
import os
|
||||
import requests
|
||||
|
||||
# ID token is available as environment variable
|
||||
id_token = os.environ.get('GOOGLE_ID_TOKEN', '${{ steps.auth.outputs.id_token }}')
|
||||
|
||||
# Use the token to invoke a Cloud Run service
|
||||
response = requests.get(
|
||||
'https://myapp-uvehjacqzq.a.run.app',
|
||||
headers={'Authorization': f'Bearer {id_token}'}
|
||||
)
|
||||
print(response.text)
|
||||
"
|
||||
```
|
||||
|
||||
### Using Default Credentials with Scopes in Python
|
||||
|
||||
When using Workload Identity Federation with Python libraries, you may need to
|
||||
add scopes before refreshing credentials:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
job_id:
|
||||
permissions:
|
||||
contents: 'read'
|
||||
id-token: 'write'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
|
||||
- id: 'auth'
|
||||
uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
service_account: 'my-service-account@my-project.iam.gserviceaccount.com'
|
||||
|
||||
- id: 'python-auth'
|
||||
run: |-
|
||||
python -c "
|
||||
from google.auth import default
|
||||
from google.auth.transport.requests import Request
|
||||
|
||||
# Get default credentials
|
||||
credentials, project = default()
|
||||
|
||||
# Add scopes before refreshing for impersonation
|
||||
credentials = credentials.with_scopes(
|
||||
['https://www.googleapis.com/auth/cloud-platform']
|
||||
)
|
||||
|
||||
# Refresh to get the token
|
||||
credentials.refresh(request=Request())
|
||||
|
||||
# Now you can use the credentials
|
||||
print(f'Access token: {credentials.token}')
|
||||
if hasattr(credentials, 'id_token'):
|
||||
print(f'ID token: {credentials.id_token}')
|
||||
"
|
||||
```
|
||||
|
||||
[github-markdown-toc]: https://github.blog/changelog/2021-04-13-table-of-contents-support-in-markdown-files/
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ curl -sfL -H "Accept: application/json" "https://api.github.com/repos/${REPO}" |
|
|||
These can be used in an Attribute Condition:
|
||||
|
||||
```cel
|
||||
assertion.repository_owner_id == 1342004 && assertion.repository_id == 260064828
|
||||
assertion.repository_owner_id == '1342004' && assertion.repository_id == '260064828'
|
||||
```
|
||||
|
||||
[cybersquatting]: https://en.wikipedia.org/wiki/Cybersquatting
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@
|
|||
see exactly which step is failing. Ensure you are using the latest version
|
||||
of the GitHub Action.
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Enabling debug logging increases the chances of a secret
|
||||
> being accidentially logged. While GitHub Actions will scrub secrets,
|
||||
> **⚠️ WARNING!** Enabling debug logging increases the chances of a secret
|
||||
> being accidentally logged. While GitHub Actions will scrub secrets,
|
||||
> please take extra caution when sharing these debug logs in publicly
|
||||
> accessible places like GitHub issues.
|
||||
>
|
||||
|
|
@ -29,7 +27,7 @@
|
|||
```yaml
|
||||
steps:
|
||||
- uses: 'actions/checkout@v4'
|
||||
- uses: 'google-github-actions/auth@v2'
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
```
|
||||
|
||||
1. Ensure the value for `workload_identity_provider` is the full _Provider_
|
||||
|
|
@ -46,7 +44,7 @@
|
|||
|
||||
```diff
|
||||
- projects/my-project/locations/global/workloadIdentityPools/my-pool/providers/my-provider
|
||||
+ projects/1234567890/locations/global/workloadIdentityPools/my-pool/providers/
|
||||
+ projects/1234567890/locations/global/workloadIdentityPools/my-pool/providers/my-provider
|
||||
```
|
||||
|
||||
1. Ensure that you have the correct `permissions:` for the job in your
|
||||
|
|
@ -64,11 +62,9 @@
|
|||
GitHub OIDC token. You cannot grant permissions on an attribute unless you
|
||||
map that value from the incoming GitHub OIDC token.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Use the [GitHub Actions OIDC Debugger][oidc-debugger] to print the list of
|
||||
> token claims and compare them to your Attribute Mappings and Attribute
|
||||
> Conditions.
|
||||
> **ℹ️ TIP!** Use the [GitHub Actions OIDC Debugger][oidc-debugger] to print
|
||||
> the list of token claims and compare them to your Attribute Mappings and
|
||||
> Attribute Conditions.
|
||||
|
||||
1. Ensure you have the correct character casing and capitalization. GitHub does
|
||||
not distinguish between "foobar" and "FooBar", but Google Cloud does. Ensure
|
||||
|
|
@ -81,7 +77,7 @@
|
|||
token", it means admission into the Workload Identity Pool failed. Check
|
||||
your [**Attribute Conditions**][attribute-conditions].
|
||||
|
||||
- If the error message inclues "Failed to generate OAuth 2.0 Access
|
||||
- If the error message includes "Failed to generate OAuth 2.0 Access
|
||||
Token", it means Service Account Impersonation failed. Check your
|
||||
[**Service Account Impersonation**][sa-impersonation] settings and
|
||||
ensure the principalSet is correct.
|
||||
|
|
@ -89,10 +85,8 @@
|
|||
1. Enable `Admin Read`, `Data Read`, and `Data Write` [Audit Logging][cal] for
|
||||
Identity and Access Management (IAM) in your Google Cloud project.
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> This will increase log volume which may increase costs. You can disable
|
||||
> this audit logging after you have debugged the issue.
|
||||
> **⚠️ WARNING!** This will increase log volume which may increase costs.
|
||||
> You can disable this audit logging after you have debugged the issue.
|
||||
|
||||
Try to authenticate again, and then explore the logs for your Workload
|
||||
Identity Provider and Workload Identity Pool. Sometimes these error messages
|
||||
|
|
@ -236,11 +230,56 @@ tool like `jq`:
|
|||
cat credentials.json | jq -r tostring
|
||||
```
|
||||
|
||||
<a name="cannot-refresh"></a>
|
||||
|
||||
## Cannot refresh credentials to retrieve an ID token
|
||||
|
||||
If you get an error like:
|
||||
|
||||
```text
|
||||
google.auth.exceptions.RefreshError: ('Unable to acquire impersonated credentials', '{"error": {"code": 400, "message": "Request contains an invalid argument.", "status": "INVALID_ARGUMENT"}}')
|
||||
```
|
||||
|
||||
when trying to refresh credentials in Python code to get an ID token, this is
|
||||
usually because the credentials are missing required scopes. The Google Auth
|
||||
library requires scopes to be set when refreshing credentials for impersonation.
|
||||
|
||||
To fix this issue, add the required scopes before refreshing:
|
||||
|
||||
```python
|
||||
from google.auth import default
|
||||
from google.auth.transport.requests import Request
|
||||
|
||||
credentials, project = default()
|
||||
|
||||
# Add scopes before refreshing
|
||||
credentials = credentials.with_scopes(
|
||||
["https://www.googleapis.com/auth/cloud-platform"]
|
||||
)
|
||||
credentials.refresh(request=Request())
|
||||
|
||||
# Now you can access the ID token
|
||||
print(credentials.id_token)
|
||||
```
|
||||
|
||||
Alternatively, you can use the `token_format` parameter of this action to
|
||||
generate an ID token directly:
|
||||
|
||||
```yaml
|
||||
- uses: 'google-github-actions/auth@v3'
|
||||
with:
|
||||
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
|
||||
service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
|
||||
token_format: 'id_token'
|
||||
id_token_audience: 'https://example.com'
|
||||
```
|
||||
|
||||
This will export the ID token as an environment variable that you can use in
|
||||
your Python code.
|
||||
|
||||
## Organizational Policy Constraints
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Your Google Cloud organization administrator controls these
|
||||
> **ℹ️ NOTE!** Your Google Cloud organization administrator controls these
|
||||
> policies. You must work with your internal IT department to resolve OrgPolicy
|
||||
> violations and constraints.
|
||||
|
||||
|
|
|
|||
18
eslint.config.mjs
Normal file
18
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import js from '@eslint/js';
|
||||
import ts from 'typescript-eslint';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
|
||||
import prettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||
|
||||
export default ts.config(
|
||||
js.configs.recommended,
|
||||
ts.configs.eslintRecommended,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
},
|
||||
},
|
||||
{ ignores: ['dist/', '**/*.js'] },
|
||||
prettierRecommended,
|
||||
);
|
||||
1456
package-lock.json
generated
1456
package-lock.json
generated
File diff suppressed because it is too large
Load diff
38
package.json
38
package.json
|
|
@ -1,13 +1,17 @@
|
|||
{
|
||||
"name": "@google-github-actions/auth",
|
||||
"version": "2.1.2",
|
||||
"version": "3.0.0",
|
||||
"description": "Authenticate to Google Cloud using OIDC tokens or JSON service account keys.",
|
||||
"main": "dist/main/index.js",
|
||||
"scripts": {
|
||||
"build": "ncc build -m src/main.ts -o dist/main && ncc build -m src/post.ts -o dist/post",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
"format": "prettier --write **/*.ts",
|
||||
"test": "bash ./bin/runTests.sh"
|
||||
"lint": "eslint .",
|
||||
"format": "eslint . --fix",
|
||||
"test": "node --require ts-node/register --test-reporter spec --test tests/**/*.test.ts"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 24.x",
|
||||
"npm": ">= 11.x"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -23,20 +27,22 @@
|
|||
"author": "GoogleCloudPlatform",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/http-client": "^2.2.0",
|
||||
"@google-github-actions/actions-utils": "^0.7.0"
|
||||
"@actions/core": "^1.11.1",
|
||||
"@actions/http-client": "^2.2.3",
|
||||
"@google-github-actions/actions-utils": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"@vercel/ncc": "^0.38.1",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"prettier": "^3.2.4",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.34.0",
|
||||
"@types/node": "^24.3.0",
|
||||
"@vercel/ncc": "^0.38.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint": "^9.34.0",
|
||||
"prettier": "^3.6.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript-eslint": "^8.41.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.41.0",
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
import { HttpClient } from '@actions/http-client';
|
||||
|
||||
import { expandUniverseEndpoints } from '@google-github-actions/actions-utils';
|
||||
|
||||
import { Logger } from '../logger';
|
||||
import { userAgent } from '../utils';
|
||||
|
||||
|
|
@ -32,23 +34,24 @@ export interface AuthClient {
|
|||
* other Google Cloud tools) that instructs the tool how to perform identity
|
||||
* federation.
|
||||
*/
|
||||
createCredentialsFile(outputPath: string): Promise<string>;
|
||||
createCredentialsFile(outputPath: string): Promise<string>; // eslint-disable-line no-unused-vars
|
||||
|
||||
/**
|
||||
* signJWT signs a JWT using the auth provider.
|
||||
*/
|
||||
signJWT(claims: any): Promise<string>;
|
||||
signJWT(claims: any): Promise<string>; // eslint-disable-line no-unused-vars
|
||||
}
|
||||
|
||||
export interface ClientParameters {
|
||||
logger: Logger;
|
||||
universe: string;
|
||||
child: string;
|
||||
requestReason?: string;
|
||||
}
|
||||
|
||||
export class Client {
|
||||
export abstract class Client {
|
||||
protected readonly _logger: Logger;
|
||||
protected readonly _httpClient: HttpClient;
|
||||
private readonly _requestReason: string | undefined;
|
||||
|
||||
protected readonly _endpoints = {
|
||||
iam: 'https://iam.{universe}/v1',
|
||||
|
|
@ -57,10 +60,9 @@ export class Client {
|
|||
sts: 'https://sts.{universe}/v1',
|
||||
www: 'https://www.{universe}',
|
||||
};
|
||||
protected readonly _universe;
|
||||
|
||||
constructor(opts: ClientParameters) {
|
||||
this._logger = opts.logger.withNamespace(opts.child);
|
||||
constructor(child: string, opts: ClientParameters) {
|
||||
this._logger = opts.logger.withNamespace(child);
|
||||
|
||||
// Create the http client with our user agent.
|
||||
this._httpClient = new HttpClient(userAgent, undefined, {
|
||||
|
|
@ -71,26 +73,19 @@ export class Client {
|
|||
maxRetries: 3,
|
||||
});
|
||||
|
||||
// Expand universe to support TPC and custom endpoints.
|
||||
this._universe = opts.universe;
|
||||
for (const key of Object.keys(this._endpoints) as Array<keyof typeof this._endpoints>) {
|
||||
this._endpoints[key] = this.expandEndpoint(key);
|
||||
}
|
||||
this._logger.debug(`Computed endpoints for universe ${this._universe}`, this._endpoints);
|
||||
this._endpoints = expandUniverseEndpoints(this._endpoints, opts.universe);
|
||||
this._requestReason = opts.requestReason;
|
||||
}
|
||||
|
||||
expandEndpoint(key: keyof typeof this._endpoints): string {
|
||||
const envOverrideKey = `GHA_ENDPOINT_OVERRIDE_${key}`;
|
||||
const envOverrideValue = process.env[envOverrideKey];
|
||||
if (envOverrideValue && envOverrideValue !== '') {
|
||||
this._logger.debug(
|
||||
`Overriding API endpoint for ${key} because ${envOverrideKey} is set`,
|
||||
envOverrideValue,
|
||||
);
|
||||
return envOverrideValue.replace(/\/+$/, '');
|
||||
/**
|
||||
* _headers returns any added headers to apply to HTTP API calls.
|
||||
*/
|
||||
protected _headers(): Record<string, string> {
|
||||
const headers: Record<string, string> = {};
|
||||
if (this._requestReason) {
|
||||
headers['X-Goog-Request-Reason'] = this._requestReason;
|
||||
}
|
||||
|
||||
return (this._endpoints[key] || '').replace(/{universe}/g, this._universe).replace(/\/+$/, '');
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
export { IAMCredentialsClient, IAMCredentialsClientParameters } from './iamcredentials';
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ import { URLSearchParams } from 'url';
|
|||
|
||||
import { errorMessage } from '@google-github-actions/actions-utils';
|
||||
|
||||
import { Client } from './client';
|
||||
import { Logger } from '../logger';
|
||||
import { Client, ClientParameters } from './client';
|
||||
|
||||
/**
|
||||
* GenerateAccessTokenParameters are the inputs to the generateAccessToken call.
|
||||
|
|
@ -42,10 +41,7 @@ export interface GenerateIDTokenParameters {
|
|||
/**
|
||||
* IAMCredentialsClientParameters are the inputs to the IAM client.
|
||||
*/
|
||||
export interface IAMCredentialsClientParameters {
|
||||
readonly logger: Logger;
|
||||
readonly universe: string;
|
||||
|
||||
export interface IAMCredentialsClientParameters extends ClientParameters {
|
||||
readonly authToken: string;
|
||||
}
|
||||
|
||||
|
|
@ -57,11 +53,7 @@ export class IAMCredentialsClient extends Client {
|
|||
readonly #authToken: string;
|
||||
|
||||
constructor(opts: IAMCredentialsClientParameters) {
|
||||
super({
|
||||
logger: opts.logger,
|
||||
universe: opts.universe,
|
||||
child: `IAMCredentialsClient`,
|
||||
});
|
||||
super('IAMCredentialsClient', opts);
|
||||
|
||||
this.#authToken = opts.authToken;
|
||||
}
|
||||
|
|
@ -80,7 +72,9 @@ export class IAMCredentialsClient extends Client {
|
|||
|
||||
const pth = `${this._endpoints.iamcredentials}/projects/-/serviceAccounts/${serviceAccount}:generateAccessToken`;
|
||||
|
||||
const headers = { Authorization: `Bearer ${this.#authToken}` };
|
||||
const headers = Object.assign(this._headers(), {
|
||||
Authorization: `Bearer ${this.#authToken}`,
|
||||
});
|
||||
|
||||
const body: Record<string, string | Array<string>> = {};
|
||||
if (delegates && delegates.length > 0) {
|
||||
|
|
@ -126,10 +120,10 @@ export class IAMCredentialsClient extends Client {
|
|||
|
||||
const pth = `${this._endpoints.oauth2}/token`;
|
||||
|
||||
const headers = {
|
||||
const headers = Object.assign(this._headers(), {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
};
|
||||
});
|
||||
|
||||
const body = new URLSearchParams();
|
||||
body.append('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
|
||||
|
|
@ -173,7 +167,9 @@ export class IAMCredentialsClient extends Client {
|
|||
|
||||
const pth = `${this._endpoints.iamcredentials}/projects/-/serviceAccounts/${serviceAccount}:generateIdToken`;
|
||||
|
||||
const headers = { Authorization: `Bearer ${this.#authToken}` };
|
||||
const headers = Object.assign(this._headers(), {
|
||||
Authorization: `Bearer ${this.#authToken}`,
|
||||
});
|
||||
|
||||
const body: Record<string, string | string[] | boolean> = {
|
||||
audience: audience,
|
||||
|
|
|
|||
|
|
@ -23,17 +23,13 @@ import {
|
|||
writeSecureFile,
|
||||
} from '@google-github-actions/actions-utils';
|
||||
|
||||
import { AuthClient, Client } from './client';
|
||||
import { Logger } from '../logger';
|
||||
import { AuthClient, Client, ClientParameters } from './client';
|
||||
|
||||
/**
|
||||
* ServiceAccountKeyClientParameters is used as input to the
|
||||
* ServiceAccountKeyClient.
|
||||
*/
|
||||
export interface ServiceAccountKeyClientParameters {
|
||||
readonly logger: Logger;
|
||||
readonly universe: string;
|
||||
|
||||
export interface ServiceAccountKeyClientParameters extends ClientParameters {
|
||||
readonly serviceAccountKey: string;
|
||||
}
|
||||
|
||||
|
|
@ -46,11 +42,7 @@ export class ServiceAccountKeyClient extends Client implements AuthClient {
|
|||
readonly #audience: string;
|
||||
|
||||
constructor(opts: ServiceAccountKeyClientParameters) {
|
||||
super({
|
||||
logger: opts.logger,
|
||||
universe: opts.universe,
|
||||
child: `ServiceAccountKeyClient`,
|
||||
});
|
||||
super('ServiceAccountKeyClient', opts);
|
||||
|
||||
const serviceAccountKey = parseCredential(opts.serviceAccountKey);
|
||||
if (!isServiceAccountKey(serviceAccountKey)) {
|
||||
|
|
|
|||
|
|
@ -14,17 +14,13 @@
|
|||
|
||||
import { errorMessage, writeSecureFile } from '@google-github-actions/actions-utils';
|
||||
|
||||
import { AuthClient, Client } from './client';
|
||||
import { Logger } from '../logger';
|
||||
import { AuthClient, Client, ClientParameters } from './client';
|
||||
|
||||
/**
|
||||
* WorkloadIdentityFederationClientParameters is used as input to the
|
||||
* WorkloadIdentityFederationClient.
|
||||
*/
|
||||
export interface WorkloadIdentityFederationClientParameters {
|
||||
readonly logger: Logger;
|
||||
readonly universe: string;
|
||||
|
||||
export interface WorkloadIdentityFederationClientParameters extends ClientParameters {
|
||||
readonly githubOIDCToken: string;
|
||||
readonly githubOIDCTokenRequestURL: string;
|
||||
readonly githubOIDCTokenRequestToken: string;
|
||||
|
|
@ -51,11 +47,7 @@ export class WorkloadIdentityFederationClient extends Client implements AuthClie
|
|||
#cachedAt?: number;
|
||||
|
||||
constructor(opts: WorkloadIdentityFederationClientParameters) {
|
||||
super({
|
||||
logger: opts.logger,
|
||||
universe: opts.universe,
|
||||
child: `WorkloadIdentityFederationClient`,
|
||||
});
|
||||
super('WorkloadIdentityFederationClient', opts);
|
||||
|
||||
this.#githubOIDCToken = opts.githubOIDCToken;
|
||||
this.#githubOIDCTokenRequestURL = opts.githubOIDCTokenRequestURL;
|
||||
|
|
@ -90,6 +82,8 @@ export class WorkloadIdentityFederationClient extends Client implements AuthClie
|
|||
|
||||
const pth = `${this._endpoints.sts}/token`;
|
||||
|
||||
const headers = Object.assign(this._headers(), {});
|
||||
|
||||
const body = {
|
||||
audience: this.#audience,
|
||||
grantType: `urn:ietf:params:oauth:grant-type:token-exchange`,
|
||||
|
|
@ -102,11 +96,12 @@ export class WorkloadIdentityFederationClient extends Client implements AuthClie
|
|||
logger.debug(`Built request`, {
|
||||
method: `POST`,
|
||||
path: pth,
|
||||
headers: headers,
|
||||
body: body,
|
||||
});
|
||||
|
||||
try {
|
||||
const resp = await this._httpClient.postJson<{ access_token: string }>(pth, body);
|
||||
const resp = await this._httpClient.postJson<{ access_token: string }>(pth, body, headers);
|
||||
const statusCode = resp.statusCode || 500;
|
||||
if (statusCode < 200 || statusCode > 299) {
|
||||
throw new Error(`Failed to call ${pth}: HTTP ${statusCode}: ${resp.result || '[no body]'}`);
|
||||
|
|
@ -140,9 +135,9 @@ export class WorkloadIdentityFederationClient extends Client implements AuthClie
|
|||
|
||||
const pth = `${this._endpoints.iamcredentials}/projects/-/serviceAccounts/${this.#serviceAccount}:signJwt`;
|
||||
|
||||
const headers = {
|
||||
const headers = Object.assign(this._headers(), {
|
||||
Authorization: `Bearer ${await this.getToken()}`,
|
||||
};
|
||||
});
|
||||
|
||||
const body = {
|
||||
payload: claims,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
* LoggerFunction is the type signature of a log function for the GitHub Actions
|
||||
* SDK.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
type LoggerFunction = (message: string, properties?: AnnotationProperties) => void;
|
||||
|
||||
/**
|
||||
|
|
|
|||
21
src/main.ts
21
src/main.ts
|
|
@ -16,7 +16,6 @@ import { join as pathjoin } from 'path';
|
|||
|
||||
import {
|
||||
exportVariable,
|
||||
getBooleanInput,
|
||||
getIDToken,
|
||||
getInput,
|
||||
setFailed,
|
||||
|
|
@ -29,8 +28,10 @@ import {
|
|||
isEmptyDir,
|
||||
isPinnedToHead,
|
||||
parseMultilineCSV,
|
||||
parseBoolean,
|
||||
parseDuration,
|
||||
pinnedToHeadWarning,
|
||||
withRetries,
|
||||
} from '@google-github-actions/actions-utils';
|
||||
|
||||
import {
|
||||
|
|
@ -79,11 +80,12 @@ export async function run(logger: Logger) {
|
|||
const oidcTokenAudience =
|
||||
getInput(`audience`) || `https://iam.googleapis.com/${workloadIdentityProvider}`;
|
||||
const credentialsJSON = getInput(`credentials_json`);
|
||||
const createCredentialsFile = getBooleanInput(`create_credentials_file`);
|
||||
const exportEnvironmentVariables = getBooleanInput(`export_environment_variables`);
|
||||
const createCredentialsFile = parseBoolean(getInput(`create_credentials_file`));
|
||||
const exportEnvironmentVariables = parseBoolean(getInput(`export_environment_variables`));
|
||||
const tokenFormat = getInput(`token_format`);
|
||||
const delegates = parseMultilineCSV(getInput(`delegates`));
|
||||
const universe = getInput(`universe`);
|
||||
const requestReason = getInput(`request_reason`);
|
||||
|
||||
// Ensure exactly one of workload_identity_provider and credentials_json was
|
||||
// provided.
|
||||
|
|
@ -109,10 +111,16 @@ export async function run(logger: Logger) {
|
|||
throw new Error(oidcWarning);
|
||||
}
|
||||
|
||||
const oidcToken = await getIDToken(oidcTokenAudience);
|
||||
const oidcToken = await withRetries(
|
||||
async (): Promise<string> => {
|
||||
return await getIDToken(oidcTokenAudience);
|
||||
},
|
||||
{ retries: 3 },
|
||||
)();
|
||||
client = new WorkloadIdentityFederationClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
requestReason: requestReason,
|
||||
|
||||
githubOIDCToken: oidcToken,
|
||||
githubOIDCTokenRequestURL: oidcTokenRequestURL,
|
||||
|
|
@ -126,6 +134,7 @@ export async function run(logger: Logger) {
|
|||
client = new ServiceAccountKeyClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
requestReason: requestReason,
|
||||
|
||||
serviceAccountKey: credentialsJSON,
|
||||
});
|
||||
|
|
@ -199,7 +208,7 @@ export async function run(logger: Logger) {
|
|||
// Set the project ID environment variables to the computed values.
|
||||
if (!projectID) {
|
||||
logger.info(
|
||||
`⚠️ Failed to a project ID from the given inputs. Neither the ` +
|
||||
`⚠️ Failed to compute a project ID from the given inputs. Neither the ` +
|
||||
`"project_id" output nor any environment variables will be ` +
|
||||
`exported. If you require these values in other steps, specify the ` +
|
||||
`"project_id" input directly.`,
|
||||
|
|
@ -298,7 +307,7 @@ export async function run(logger: Logger) {
|
|||
logger.debug(`Creating id token`);
|
||||
|
||||
const idTokenAudience = getInput('id_token_audience', { required: true });
|
||||
const idTokenIncludeEmail = getBooleanInput('id_token_include_email');
|
||||
const idTokenIncludeEmail = parseBoolean(getInput('id_token_include_email'));
|
||||
|
||||
// Ensure a service_account was provided if using WIF.
|
||||
if (!serviceAccount) {
|
||||
|
|
|
|||
10
src/post.ts
10
src/post.ts
|
|
@ -12,21 +12,21 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { getBooleanInput, setFailed } from '@actions/core';
|
||||
import { getInput, setFailed } from '@actions/core';
|
||||
|
||||
import { errorMessage, forceRemove } from '@google-github-actions/actions-utils';
|
||||
import { errorMessage, forceRemove, parseBoolean } from '@google-github-actions/actions-utils';
|
||||
|
||||
import { Logger } from './logger';
|
||||
|
||||
export async function run(logger: Logger) {
|
||||
try {
|
||||
const createCredentials = getBooleanInput('create_credentials_file');
|
||||
const createCredentials = parseBoolean(getInput('create_credentials_file'));
|
||||
if (!createCredentials) {
|
||||
logger.info(`Skipping credential cleanup - "create_credentials_file" is false.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanupCredentials = getBooleanInput('cleanup_credentials');
|
||||
const cleanupCredentials = parseBoolean(getInput('cleanup_credentials'));
|
||||
if (!cleanupCredentials) {
|
||||
logger.info(`Skipping credential cleanup - "cleanup_credentials" is false.`);
|
||||
return;
|
||||
|
|
@ -34,7 +34,7 @@ export async function run(logger: Logger) {
|
|||
|
||||
// Look up the credentials path, if one exists. Note that we only check the
|
||||
// environment variable set by our action, since we don't want to
|
||||
// accidentially clean up if someone set GOOGLE_APPLICATION_CREDENTIALS or
|
||||
// accidentally clean up if someone set GOOGLE_APPLICATION_CREDENTIALS or
|
||||
// another environment variable manually.
|
||||
const credentialsPath = process.env['GOOGLE_GHA_CREDS_PATH'];
|
||||
if (!credentialsPath) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import {
|
|||
} from '@google-github-actions/actions-utils';
|
||||
|
||||
// Do not listen to the linter - this can NOT be rewritten as an ES6 import statement.
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
export const { version: appVersion } = require('../package.json');
|
||||
|
||||
// userAgent is the default user agent.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"alwaysStrict": true,
|
||||
"target": "es6",
|
||||
"target": "es2022",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"lib": ["es2022"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue