Refactor to support access and id tokens (#3)
This commit is contained in:
parent
afef6a5b6d
commit
cb396c3f31
62
.github/workflows/test.yaml
vendored
62
.github/workflows/test.yaml
vendored
@ -9,13 +9,34 @@ on:
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: 'test'
|
||||
unit:
|
||||
name: 'unit'
|
||||
runs-on: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/checkout@v2'
|
||||
|
||||
- uses: 'actions/setup-node@master'
|
||||
with:
|
||||
node-version: '12.x'
|
||||
|
||||
- name: 'npm install'
|
||||
run: 'npm install'
|
||||
|
||||
- name: 'npm lint'
|
||||
run: 'npm run lint'
|
||||
|
||||
- name: 'npm test'
|
||||
run: 'npm run test'
|
||||
|
||||
access_token:
|
||||
name: 'access_token'
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
runs-on: '${{ matrix.operating-system }}'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
operating-system:
|
||||
- 'ubuntu-latest'
|
||||
@ -28,19 +49,40 @@ jobs:
|
||||
with:
|
||||
node-version: '12.x'
|
||||
|
||||
- id: 'integration'
|
||||
- id: 'access-token'
|
||||
name: 'integration'
|
||||
uses: './'
|
||||
with:
|
||||
token_format: 'access_token'
|
||||
workload_identity_provider: 'projects/469401941463/locations/global/workloadIdentityPools/github-actions/providers/github-oidc-auth-google-cloud'
|
||||
service_account: 'github-secret-accessor@actions-oidc-test.iam.gserviceaccount.com'
|
||||
id_token_audience: 'foo'
|
||||
|
||||
- name: 'npm install'
|
||||
run: 'npm install'
|
||||
id_token:
|
||||
name: 'id_token'
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
runs-on: '${{ matrix.operating-system }}'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
operating-system:
|
||||
- 'ubuntu-latest'
|
||||
- 'windows-latest'
|
||||
- 'macos-latest'
|
||||
steps:
|
||||
- uses: 'actions/checkout@v2'
|
||||
|
||||
- name: 'npm lint'
|
||||
run: 'npm run lint'
|
||||
- uses: 'actions/setup-node@master'
|
||||
with:
|
||||
node-version: '12.x'
|
||||
|
||||
- name: 'npm test'
|
||||
run: 'npm run test'
|
||||
- id: 'id-token'
|
||||
name: 'integration'
|
||||
uses: './'
|
||||
with:
|
||||
token_format: 'id_token'
|
||||
workload_identity_provider: 'projects/469401941463/locations/global/workloadIdentityPools/github-actions/providers/github-oidc-auth-google-cloud'
|
||||
service_account: 'github-secret-accessor@actions-oidc-test.iam.gserviceaccount.com'
|
||||
id_token_audience: 'my-aud'
|
||||
id_token_include_email: true
|
||||
|
28
README.md
28
README.md
@ -41,6 +41,7 @@ jobs:
|
||||
name: 'Authenticate to Google Cloud'
|
||||
uses: 'github.com/sethvargo/oidc-auth-google-cloud'
|
||||
with:
|
||||
token_format: 'access_token'
|
||||
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
|
||||
service_account: 'my-service-account@my-project.iam.gserviceaccount.com'
|
||||
|
||||
@ -74,23 +75,40 @@ jobs:
|
||||
`"sigstore"`, but this variable exists in case custom values are permitted
|
||||
in the future. The default value is `"sigstore"`.
|
||||
|
||||
- `token_format`: (Optional) Format of the generated token. For OAuth 2.0
|
||||
access tokens, specify "access_token". For OIDC tokens, specify "id_token".
|
||||
The default value is "access_token".
|
||||
|
||||
- `delegates`: (Optional) List of additional service account emails or unique
|
||||
identities to use for impersonation in the chain. By default there are no
|
||||
delegates.
|
||||
|
||||
- `lifetime`: (Optional) Desired lifetime duration of the access token, in
|
||||
seconds. This must be specified as the number of seconds with a trailing "s"
|
||||
(e.g. 30s). The default value is 1 hour (3600s).
|
||||
- `access_token_lifetime`: (Optional) Desired lifetime duration of the access
|
||||
token, in seconds. This must be specified as the number of seconds with a
|
||||
trailing "s" (e.g. 30s). The default value is 1 hour (3600s).
|
||||
|
||||
- `access_token_scopes`: (Optional) List of OAuth 2.0 access scopes to be
|
||||
included in the generated token. This is only valid when "token_format" is
|
||||
"access_token". The default value is:
|
||||
|
||||
```text
|
||||
https://www.googleapis.com/auth/cloud-platform
|
||||
```
|
||||
|
||||
- `id_token_audience`: (Optional) The audience for the generated ID Token.
|
||||
|
||||
- `id_token_include_email`: (Optional) Optional parameter of whether to
|
||||
include the service account email in the generated token. If true, the token
|
||||
will contain "email" and "email_verified" claims. This is only valid when
|
||||
"token_format" is "access_token". The default value is false.
|
||||
|
||||
## Outputs
|
||||
|
||||
- `access_token`: The authenticated Google Cloud access token for calling
|
||||
other Google Cloud APIs.
|
||||
|
||||
- `expiration`: The RFC3339 UTC "Zulu" format timestamp when the token
|
||||
expires.
|
||||
- `access_token_expiration`: The RFC3339 UTC "Zulu" format timestamp when the
|
||||
token expires.
|
||||
|
||||
- `id_token`: The authenticated Google Cloud ID token. This token is only
|
||||
generated when `id_token_audience` input parameter is provided.
|
||||
|
47
action.yml
47
action.yml
@ -15,8 +15,8 @@
|
||||
name: 'OIDC Authenticate to Google Cloud'
|
||||
author: 'sethvargo'
|
||||
description: |-
|
||||
Authenticate to Google Cloud from GitHub Actions using an OIDC token and
|
||||
Workload Identity Federation.
|
||||
Generate credentials to authenticate to Google Cloud from GitHub Actions using
|
||||
an OIDC token and Workload Identity Federation.
|
||||
|
||||
inputs:
|
||||
workload_identity_provider:
|
||||
@ -38,35 +38,62 @@ inputs:
|
||||
exists in case custom values are permitted in the future.
|
||||
default: 'sigstore'
|
||||
required: false
|
||||
token_format:
|
||||
description: |-
|
||||
Format for the generated token. For OAuth 2.0 access tokens, specify
|
||||
"access_token". For OIDC tokens, specify "id_token".
|
||||
default: 'access_token'
|
||||
required: true
|
||||
delegates:
|
||||
description: |-
|
||||
List of additional service account emails or unique identities to use for
|
||||
impersonation in the chain.
|
||||
default: ''
|
||||
required: false
|
||||
lifetime:
|
||||
|
||||
# access token params
|
||||
access_token_lifetime:
|
||||
description: |-
|
||||
Desired lifetime duration of the access token, in seconds. This must be
|
||||
specified as the number of seconds with a trailing "s" (e.g. 30s).
|
||||
specified as the number of seconds with a trailing "s" (e.g. 30s). This is
|
||||
only valid when "token_format" is "access_token".
|
||||
default: '3600s'
|
||||
required: false
|
||||
access_token_scopes:
|
||||
description: |-
|
||||
List of OAuth 2.0 access scopes to be included in the generated token.
|
||||
This is only valid when "token_format" is "access_token".
|
||||
default: 'https://www.googleapis.com/auth/cloud-platform'
|
||||
|
||||
# id token params
|
||||
id_token_audience:
|
||||
description: |-
|
||||
The audience for the generated Google Cloud ID Token.
|
||||
The audience (aud) for the generated Google Cloud ID Token. This is only
|
||||
valid when "token_format" is "id_token".
|
||||
default: ''
|
||||
required: false
|
||||
id_token_include_email:
|
||||
description: |-
|
||||
Optional parameter of whether to include the service account email in the
|
||||
generated token. If true, the token will contain "email" and
|
||||
"email_verified" claims. This is only valid when "token_format" is
|
||||
"access_token".
|
||||
default: false
|
||||
required: false
|
||||
|
||||
outputs:
|
||||
access_token:
|
||||
description: |-
|
||||
The Google Cloud access token for calling other Google Cloud APIs.
|
||||
expiration:
|
||||
The Google Cloud access token for calling other Google Cloud APIs. This
|
||||
is only available when "token_format" is "access_token".
|
||||
access_token_expiration:
|
||||
description: |-
|
||||
The expiration timestamp for the access token.
|
||||
The expiration timestamp for the access token. This is only available
|
||||
when "token_format" is "access_token".
|
||||
id_token:
|
||||
description: |-
|
||||
The Google Cloud ID token. This token is only generated when
|
||||
`id_token_audience` input parameter was provided.
|
||||
The Google Cloud ID token. This is only available when "token_format" is
|
||||
"id_token".
|
||||
|
||||
branding:
|
||||
icon: 'lock'
|
||||
|
34
dist/index.js
vendored
34
dist/index.js
vendored
@ -225,37 +225,51 @@ function run() {
|
||||
});
|
||||
const serviceAccount = core.getInput('service_account', { required: true });
|
||||
const audience = core.getInput('audience');
|
||||
const tokenFormat = core.getInput('token_format', { required: true });
|
||||
const delegates = explodeStrings(core.getInput('delegates'));
|
||||
const lifetime = core.getInput('lifetime');
|
||||
const accessTokenLifetime = core.getInput('access_token_lifetime');
|
||||
const accessTokenScopes = explodeStrings(core.getInput('access_token_scopes'));
|
||||
const idTokenAudience = core.getInput('id_token_audience');
|
||||
const idTokenIncludeEmail = core.getBooleanInput('id_token_include_email');
|
||||
// Get the GitHub OIDC token.
|
||||
const githubOIDCToken = yield core.getIDToken(audience);
|
||||
|
||||
// Exchange the GitHub OIDC token for a Google Federated Token.
|
||||
const googleFederatedToken = yield client_1.Client.googleFederatedToken({
|
||||
providerID: workloadIdentityProvider,
|
||||
token: githubOIDCToken,
|
||||
});
|
||||
core.setSecret(googleFederatedToken);
|
||||
switch (tokenFormat) {
|
||||
case 'access_token': {
|
||||
// Exchange the Google Federated Token for an access token.
|
||||
const { accessToken, expiration } = yield client_1.Client.googleAccessToken({
|
||||
token: googleFederatedToken,
|
||||
serviceAccount: serviceAccount,
|
||||
delegates: delegates,
|
||||
lifetime: lifetime,
|
||||
lifetime: accessTokenLifetime,
|
||||
scopes: accessTokenScopes,
|
||||
});
|
||||
core.setSecret(accessToken);
|
||||
core.setOutput('access_token', accessToken);
|
||||
core.setOutput('expiration', expiration);
|
||||
// Exchange the Google Federated Token for an ID token.
|
||||
if (idTokenAudience != '') {
|
||||
core.setOutput('access_token_expiration', expiration);
|
||||
break;
|
||||
}
|
||||
case 'id_token': {
|
||||
// Exchange the Google Federated Token for an id token.
|
||||
const { token } = yield client_1.Client.googleIDToken({
|
||||
token: googleFederatedToken,
|
||||
serviceAccount: serviceAccount,
|
||||
delegates: delegates,
|
||||
audience: idTokenAudience,
|
||||
includeEmail: idTokenIncludeEmail,
|
||||
});
|
||||
core.setSecret(token);
|
||||
core.setOutput('id_token', token);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`unknown token format "${tokenFormat}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
@ -1880,14 +1894,14 @@ class Client {
|
||||
* googleAccessToken generates a Google Cloud access token for the provided
|
||||
* service account email or unique id.
|
||||
*/
|
||||
static googleAccessToken({ token, serviceAccount, delegates, lifetime, }) {
|
||||
static googleAccessToken({ token, serviceAccount, delegates, scopes, lifetime, }) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const serviceAccountID = `projects/-/serviceAccounts/${serviceAccount}`;
|
||||
const tokenURL = new url_1.URL(`https://iamcredentials.googleapis.com/v1/${serviceAccountID}:generateAccessToken`);
|
||||
const data = {
|
||||
delegates: delegates,
|
||||
scope: 'https://www.googleapis.com/auth/cloud-platform',
|
||||
lifetime: lifetime,
|
||||
scope: scopes,
|
||||
};
|
||||
const opts = {
|
||||
hostname: tokenURL.hostname,
|
||||
@ -1917,14 +1931,14 @@ class Client {
|
||||
* googleIDToken generates a Google Cloud ID token for the provided
|
||||
* service account email or unique id.
|
||||
*/
|
||||
static googleIDToken({ token, serviceAccount, audience, delegates, }) {
|
||||
static googleIDToken({ token, serviceAccount, audience, delegates, includeEmail, }) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const serviceAccountID = `projects/-/serviceAccounts/${serviceAccount}`;
|
||||
const tokenURL = new url_1.URL(`https://iamcredentials.googleapis.com/v1/${serviceAccountID}:generateIdToken`);
|
||||
const data = {
|
||||
delegates: delegates,
|
||||
audience: audience,
|
||||
includeEmail: true,
|
||||
includeEmail: includeEmail,
|
||||
};
|
||||
const opts = {
|
||||
hostname: tokenURL.hostname,
|
||||
|
36
package-lock.json
generated
36
package-lock.json
generated
@ -55,9 +55,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz",
|
||||
"integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==",
|
||||
"version": "7.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
|
||||
"integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@ -287,9 +287,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
|
||||
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
|
||||
"version": "16.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.2.tgz",
|
||||
"integrity": "sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
@ -1975,9 +1975,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz",
|
||||
"integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
@ -2714,9 +2714,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz",
|
||||
"integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==",
|
||||
"version": "7.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
|
||||
"integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/highlight": {
|
||||
@ -2906,9 +2906,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "16.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
|
||||
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
|
||||
"version": "16.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.2.tgz",
|
||||
"integrity": "sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
@ -4115,9 +4115,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz",
|
||||
"integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
||||
"dev": true
|
||||
},
|
||||
"prettier-linter-helpers": {
|
||||
|
@ -34,6 +34,7 @@ interface GoogleAccessTokenParameters {
|
||||
token: string;
|
||||
serviceAccount: string;
|
||||
delegates?: Array<string>;
|
||||
scopes?: Array<string>;
|
||||
lifetime?: string;
|
||||
}
|
||||
|
||||
@ -68,6 +69,7 @@ interface GoogleIDTokenParameters {
|
||||
serviceAccount: string;
|
||||
audience: string;
|
||||
delegates?: Array<string>;
|
||||
includeEmail?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,6 +165,7 @@ export class Client {
|
||||
token,
|
||||
serviceAccount,
|
||||
delegates,
|
||||
scopes,
|
||||
lifetime,
|
||||
}: GoogleAccessTokenParameters): Promise<GoogleAccessTokenResponse> {
|
||||
const serviceAccountID = `projects/-/serviceAccounts/${serviceAccount}`;
|
||||
@ -172,8 +175,8 @@ export class Client {
|
||||
|
||||
const data = {
|
||||
delegates: delegates,
|
||||
scope: 'https://www.googleapis.com/auth/cloud-platform',
|
||||
lifetime: lifetime,
|
||||
scope: scopes,
|
||||
};
|
||||
|
||||
const opts = {
|
||||
@ -209,6 +212,7 @@ export class Client {
|
||||
serviceAccount,
|
||||
audience,
|
||||
delegates,
|
||||
includeEmail,
|
||||
}: GoogleIDTokenParameters): Promise<GoogleIDTokenResponse> {
|
||||
const serviceAccountID = `projects/-/serviceAccounts/${serviceAccount}`;
|
||||
const tokenURL = new URL(
|
||||
@ -218,7 +222,7 @@ export class Client {
|
||||
const data = {
|
||||
delegates: delegates,
|
||||
audience: audience,
|
||||
includeEmail: true,
|
||||
includeEmail: includeEmail,
|
||||
};
|
||||
|
||||
const opts = {
|
||||
|
26
src/main.ts
26
src/main.ts
@ -35,10 +35,14 @@ async function run(): Promise<void> {
|
||||
});
|
||||
const serviceAccount = core.getInput('service_account', { required: true });
|
||||
const audience = core.getInput('audience');
|
||||
const tokenFormat = core.getInput('token_format', { required: true });
|
||||
const delegates = explodeStrings(core.getInput('delegates'));
|
||||
const lifetime = core.getInput('lifetime');
|
||||
const accessTokenLifetime = core.getInput('access_token_lifetime');
|
||||
const accessTokenScopes = explodeStrings(core.getInput('access_token_scopes'));
|
||||
const idTokenAudience = core.getInput('id_token_audience');
|
||||
const idTokenIncludeEmail = core.getBooleanInput('id_token_include_email');
|
||||
|
||||
// Get the GitHub OIDC token.
|
||||
const githubOIDCToken = await core.getIDToken(audience);
|
||||
|
||||
// Exchange the GitHub OIDC token for a Google Federated Token.
|
||||
@ -48,27 +52,37 @@ async function run(): Promise<void> {
|
||||
});
|
||||
core.setSecret(googleFederatedToken);
|
||||
|
||||
switch (tokenFormat) {
|
||||
case 'access_token': {
|
||||
// Exchange the Google Federated Token for an access token.
|
||||
const { accessToken, expiration } = await Client.googleAccessToken({
|
||||
token: googleFederatedToken,
|
||||
serviceAccount: serviceAccount,
|
||||
delegates: delegates,
|
||||
lifetime: lifetime,
|
||||
lifetime: accessTokenLifetime,
|
||||
scopes: accessTokenScopes,
|
||||
});
|
||||
core.setSecret(accessToken);
|
||||
core.setOutput('access_token', accessToken);
|
||||
core.setOutput('expiration', expiration);
|
||||
|
||||
// Exchange the Google Federated Token for an ID token.
|
||||
if (idTokenAudience != '') {
|
||||
core.setOutput('access_token_expiration', expiration);
|
||||
break;
|
||||
}
|
||||
case 'id_token': {
|
||||
// Exchange the Google Federated Token for an id token.
|
||||
const { token } = await Client.googleIDToken({
|
||||
token: googleFederatedToken,
|
||||
serviceAccount: serviceAccount,
|
||||
delegates: delegates,
|
||||
audience: idTokenAudience,
|
||||
includeEmail: idTokenIncludeEmail,
|
||||
});
|
||||
core.setSecret(token);
|
||||
core.setOutput('id_token', token);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`unknown token format "${tokenFormat}"`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
core.setFailed(`Action failed with error: ${err}`);
|
||||
|
Loading…
Reference in New Issue
Block a user