Remove retry logic (#389)
The retries make debugging strictly more complex, and it's not clear that retrying provides actual value. If we need retries in the future, we should push them down into the per-API level.
This commit is contained in:
parent
5a50e58116
commit
39c96a3f1d
10
action.yml
10
action.yml
@ -132,24 +132,28 @@ inputs:
|
||||
default: ''
|
||||
required: false
|
||||
|
||||
# retries
|
||||
# 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.
|
||||
default: '3'
|
||||
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.
|
||||
default: '250'
|
||||
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
|
||||
|
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
530
src/main.ts
530
src/main.ts
@ -31,7 +31,6 @@ import {
|
||||
parseMultilineCSV,
|
||||
parseDuration,
|
||||
pinnedToHeadWarning,
|
||||
withRetries,
|
||||
} from '@google-github-actions/actions-utils';
|
||||
|
||||
import {
|
||||
@ -59,293 +58,272 @@ const oidcWarning =
|
||||
`GitHub Actions workflow permissions are incorrect, or this job is being ` +
|
||||
`run from a fork. For more information, please see https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token`;
|
||||
|
||||
/**
|
||||
* Executes the main action.
|
||||
*/
|
||||
async function run(): Promise<void> {
|
||||
const logger = new Logger();
|
||||
|
||||
export async function run(logger: Logger) {
|
||||
// Warn if pinned to HEAD
|
||||
if (isPinnedToHead()) {
|
||||
logger.warning(pinnedToHeadWarning('v2'));
|
||||
}
|
||||
|
||||
const retries = Number(getInput('retries'));
|
||||
|
||||
// set to undefined when not provided [avoids Number('') -> 0]
|
||||
const backoff = Number(getInput('backoff')) || undefined;
|
||||
const backoffLimit = Number(getInput('backoff_limit')) || undefined;
|
||||
|
||||
try {
|
||||
const mainWithRetries = withRetries(async () => main(logger), {
|
||||
retries: retries,
|
||||
backoff: backoff,
|
||||
backoffLimit: backoffLimit,
|
||||
// Load configuration.
|
||||
const projectID = computeProjectID(
|
||||
getInput(`project_id`),
|
||||
getInput(`service_account`),
|
||||
getInput(`credentials_json`),
|
||||
);
|
||||
const workloadIdentityProvider = getInput(`workload_identity_provider`);
|
||||
const serviceAccount = computeServiceAccountEmail(
|
||||
getInput(`service_account`),
|
||||
getInput('credentials_json'),
|
||||
);
|
||||
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 tokenFormat = getInput(`token_format`);
|
||||
const delegates = parseMultilineCSV(getInput(`delegates`));
|
||||
const universe = getInput(`universe`);
|
||||
|
||||
// Ensure exactly one of workload_identity_provider and credentials_json was
|
||||
// provided.
|
||||
if (!exactlyOneOf(workloadIdentityProvider, credentialsJSON)) {
|
||||
throw new Error(
|
||||
'The GitHub Action workflow must specify exactly one of ' +
|
||||
'"workload_identity_provider" or "credentials_json"! ' +
|
||||
secretsWarning,
|
||||
);
|
||||
}
|
||||
|
||||
// Instantiate the correct client based on the provided input parameters.
|
||||
let client: AuthClient;
|
||||
if (workloadIdentityProvider) {
|
||||
logger.debug(`Using workload identity provider "${workloadIdentityProvider}"`);
|
||||
|
||||
// If we're going to do the OIDC dance, we need to make sure these values
|
||||
// are set. If they aren't, core.getIDToken() will fail and so will
|
||||
// generating the credentials file.
|
||||
const oidcTokenRequestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
||||
const oidcTokenRequestURL = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
||||
if (!oidcTokenRequestToken || !oidcTokenRequestURL) {
|
||||
throw new Error(oidcWarning);
|
||||
}
|
||||
|
||||
const oidcToken = await getIDToken(oidcTokenAudience);
|
||||
client = new WorkloadIdentityFederationClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
|
||||
githubOIDCToken: oidcToken,
|
||||
githubOIDCTokenRequestURL: oidcTokenRequestURL,
|
||||
githubOIDCTokenRequestToken: oidcTokenRequestToken,
|
||||
githubOIDCTokenAudience: oidcTokenAudience,
|
||||
workloadIdentityProviderName: workloadIdentityProvider,
|
||||
serviceAccount: serviceAccount,
|
||||
});
|
||||
} else {
|
||||
logger.debug(`Using credentials JSON`);
|
||||
client = new ServiceAccountKeyClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
|
||||
serviceAccountKey: credentialsJSON,
|
||||
});
|
||||
}
|
||||
|
||||
// Always write the credentials file first, before trying to generate
|
||||
// tokens. This will ensure the file is written even if token generation
|
||||
// fails, which means continue-on-error actions will still have the file
|
||||
// available.
|
||||
if (createCredentialsFile) {
|
||||
logger.debug(`Creating credentials file`);
|
||||
|
||||
// Note: We explicitly and intentionally export to GITHUB_WORKSPACE
|
||||
// instead of RUNNER_TEMP, because RUNNER_TEMP is not shared with
|
||||
// Docker-based actions on the filesystem. Exporting to GITHUB_WORKSPACE
|
||||
// ensures that the exported credentials are automatically available to
|
||||
// Docker-based actions without user modification.
|
||||
//
|
||||
// This has the unintended side-effect of leaking credentials over time,
|
||||
// because GITHUB_WORKSPACE is not automatically cleaned up on self-hosted
|
||||
// runners. To mitigate this issue, this action defines a post step to
|
||||
// remove any created credentials.
|
||||
const githubWorkspace = process.env.GITHUB_WORKSPACE;
|
||||
if (!githubWorkspace) {
|
||||
throw new Error('$GITHUB_WORKSPACE is not set');
|
||||
}
|
||||
|
||||
// There have been a number of issues where users have not used the
|
||||
// "actions/checkout" step before our action. Our action relies on the
|
||||
// creation of that directory; worse, if a user puts "actions/checkout"
|
||||
// after our action, it will delete the exported credential. This
|
||||
// following code does a small check to see if there are any files in the
|
||||
// directory. It emits a warning if there are no files, since there may be
|
||||
// legitimate use cases for authenticating without checking out the
|
||||
// repository.
|
||||
const githubWorkspaceIsEmpty = await isEmptyDir(githubWorkspace);
|
||||
if (githubWorkspaceIsEmpty) {
|
||||
logger.warning(
|
||||
`The "create_credentials_file" option is true, but the current ` +
|
||||
`GitHub workspace is empty. Did you forget to use ` +
|
||||
`"actions/checkout" before this step? If you do not intend to ` +
|
||||
`share authentication with future steps in this job, set ` +
|
||||
`"create_credentials_file" to false.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Create credentials file.
|
||||
const outputFile = generateCredentialsFilename();
|
||||
const outputPath = pathjoin(githubWorkspace, outputFile);
|
||||
const credentialsPath = await client.createCredentialsFile(outputPath);
|
||||
logger.info(`Created credentials file at "${credentialsPath}"`);
|
||||
|
||||
// Output to be available to future steps.
|
||||
setOutput('credentials_file_path', credentialsPath);
|
||||
|
||||
if (exportEnvironmentVariables) {
|
||||
// CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE is picked up by gcloud to
|
||||
// use a specific credential file (subject to change and equivalent to
|
||||
// auth/credential_file_override).
|
||||
exportVariable('CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE', credentialsPath);
|
||||
|
||||
// GOOGLE_APPLICATION_CREDENTIALS is used by Application Default
|
||||
// Credentials in all GCP client libraries.
|
||||
exportVariable('GOOGLE_APPLICATION_CREDENTIALS', credentialsPath);
|
||||
|
||||
// GOOGLE_GHA_CREDS_PATH is used by other Google GitHub Actions.
|
||||
exportVariable('GOOGLE_GHA_CREDS_PATH', credentialsPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the project ID environment variables to the computed values.
|
||||
if (!projectID) {
|
||||
logger.warning(
|
||||
`Unable to compute project ID from inputs, skipping export. Please ` +
|
||||
`specify the "project_id" input directly.`,
|
||||
);
|
||||
} else {
|
||||
setOutput('project_id', projectID);
|
||||
|
||||
if (exportEnvironmentVariables) {
|
||||
exportVariable('CLOUDSDK_CORE_PROJECT', projectID);
|
||||
exportVariable('CLOUDSDK_PROJECT', projectID);
|
||||
exportVariable('GCLOUD_PROJECT', projectID);
|
||||
exportVariable('GCP_PROJECT', projectID);
|
||||
exportVariable('GOOGLE_CLOUD_PROJECT', projectID);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to generate a token. This will ensure the action correctly errors
|
||||
// if the credentials are misconfigured. This is also required so the value
|
||||
// can be set as an output for future authentication calls.
|
||||
const authToken = await client.getToken();
|
||||
logger.debug(`Successfully generated auth token`);
|
||||
setSecret(authToken);
|
||||
setOutput('auth_token', authToken);
|
||||
|
||||
// Create the credential client, we might not use it, but it's basically free.
|
||||
const iamCredentialsClient = new IAMCredentialsClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
|
||||
authToken: authToken,
|
||||
});
|
||||
|
||||
await mainWithRetries();
|
||||
switch (tokenFormat) {
|
||||
case '': {
|
||||
break;
|
||||
}
|
||||
case null: {
|
||||
break;
|
||||
}
|
||||
case 'access_token': {
|
||||
logger.debug(`Creating access token`);
|
||||
|
||||
const accessTokenLifetime = parseDuration(getInput('access_token_lifetime'));
|
||||
const accessTokenScopes = parseMultilineCSV(getInput('access_token_scopes'));
|
||||
const accessTokenSubject = getInput('access_token_subject');
|
||||
|
||||
// Ensure a service_account was provided if using WIF.
|
||||
if (!serviceAccount) {
|
||||
throw new Error(
|
||||
'The GitHub Action workflow must specify a "service_account" to ' +
|
||||
'use when generating an OAuth 2.0 Access Token. ' +
|
||||
secretsWarning,
|
||||
);
|
||||
}
|
||||
|
||||
// If a subject was provided, use the traditional OAuth 2.0 flow to
|
||||
// perform Domain-Wide Delegation. Otherwise, use the modern IAM
|
||||
// Credentials endpoints.
|
||||
let accessToken;
|
||||
if (accessTokenSubject) {
|
||||
if (accessTokenLifetime > 3600) {
|
||||
logger.info(
|
||||
`An access token subject was specified, triggering Domain-Wide ` +
|
||||
`Delegation flow. This flow does not support specifying an ` +
|
||||
`access token lifetime of greater than 1 hour.`,
|
||||
);
|
||||
}
|
||||
|
||||
const unsignedJWT = buildDomainWideDelegationJWT(
|
||||
serviceAccount,
|
||||
accessTokenSubject,
|
||||
accessTokenScopes,
|
||||
accessTokenLifetime,
|
||||
);
|
||||
const signedJWT = await client.signJWT(unsignedJWT);
|
||||
|
||||
accessToken =
|
||||
await iamCredentialsClient.generateDomainWideDelegationAccessToken(signedJWT);
|
||||
} else {
|
||||
accessToken = await iamCredentialsClient.generateAccessToken({
|
||||
serviceAccount,
|
||||
delegates,
|
||||
scopes: accessTokenScopes,
|
||||
lifetime: accessTokenLifetime,
|
||||
});
|
||||
}
|
||||
|
||||
setSecret(accessToken);
|
||||
setOutput('access_token', accessToken);
|
||||
break;
|
||||
}
|
||||
case 'id_token': {
|
||||
logger.debug(`Creating id token`);
|
||||
|
||||
const idTokenAudience = getInput('id_token_audience', { required: true });
|
||||
const idTokenIncludeEmail = getBooleanInput('id_token_include_email');
|
||||
|
||||
// Ensure a service_account was provided if using WIF.
|
||||
if (!serviceAccount) {
|
||||
throw new Error(
|
||||
'The GitHub Action workflow must specify a "service_account" to ' +
|
||||
'use when generating an OAuth 2.0 Access Token. ' +
|
||||
secretsWarning,
|
||||
);
|
||||
}
|
||||
|
||||
const idToken = await iamCredentialsClient.generateIDToken({
|
||||
serviceAccount,
|
||||
audience: idTokenAudience,
|
||||
delegates,
|
||||
includeEmail: idTokenIncludeEmail,
|
||||
});
|
||||
setSecret(idToken);
|
||||
setOutput('id_token', idToken);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown token format "${tokenFormat}"`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = errorMessage(err);
|
||||
setFailed(`google-github-actions/auth failed with: ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main wraps the main action logic into a function to be used as a parameter to the withRetries function.
|
||||
*/
|
||||
async function main(logger: Logger) {
|
||||
// Load configuration.
|
||||
const projectID = computeProjectID(
|
||||
getInput(`project_id`),
|
||||
getInput(`service_account`),
|
||||
getInput(`credentials_json`),
|
||||
);
|
||||
const workloadIdentityProvider = getInput(`workload_identity_provider`);
|
||||
const serviceAccount = computeServiceAccountEmail(
|
||||
getInput(`service_account`),
|
||||
getInput('credentials_json'),
|
||||
);
|
||||
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 tokenFormat = getInput(`token_format`);
|
||||
const delegates = parseMultilineCSV(getInput(`delegates`));
|
||||
const universe = getInput(`universe`);
|
||||
|
||||
// Ensure exactly one of workload_identity_provider and credentials_json was
|
||||
// provided.
|
||||
if (!exactlyOneOf(workloadIdentityProvider, credentialsJSON)) {
|
||||
throw new Error(
|
||||
'The GitHub Action workflow must specify exactly one of ' +
|
||||
'"workload_identity_provider" or "credentials_json"! ' +
|
||||
secretsWarning,
|
||||
);
|
||||
}
|
||||
|
||||
// Instantiate the correct client based on the provided input parameters.
|
||||
let client: AuthClient;
|
||||
if (workloadIdentityProvider) {
|
||||
logger.debug(`Using workload identity provider "${workloadIdentityProvider}"`);
|
||||
|
||||
// If we're going to do the OIDC dance, we need to make sure these values
|
||||
// are set. If they aren't, core.getIDToken() will fail and so will
|
||||
// generating the credentials file.
|
||||
const oidcTokenRequestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
||||
const oidcTokenRequestURL = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
||||
if (!oidcTokenRequestToken || !oidcTokenRequestURL) {
|
||||
throw new Error(oidcWarning);
|
||||
}
|
||||
|
||||
const oidcToken = await getIDToken(oidcTokenAudience);
|
||||
client = new WorkloadIdentityFederationClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
|
||||
githubOIDCToken: oidcToken,
|
||||
githubOIDCTokenRequestURL: oidcTokenRequestURL,
|
||||
githubOIDCTokenRequestToken: oidcTokenRequestToken,
|
||||
githubOIDCTokenAudience: oidcTokenAudience,
|
||||
workloadIdentityProviderName: workloadIdentityProvider,
|
||||
serviceAccount: serviceAccount,
|
||||
});
|
||||
} else {
|
||||
logger.debug(`Using credentials JSON`);
|
||||
client = new ServiceAccountKeyClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
|
||||
serviceAccountKey: credentialsJSON,
|
||||
});
|
||||
}
|
||||
|
||||
// Always write the credentials file first, before trying to generate
|
||||
// tokens. This will ensure the file is written even if token generation
|
||||
// fails, which means continue-on-error actions will still have the file
|
||||
// available.
|
||||
if (createCredentialsFile) {
|
||||
logger.debug(`Creating credentials file`);
|
||||
|
||||
// Note: We explicitly and intentionally export to GITHUB_WORKSPACE
|
||||
// instead of RUNNER_TEMP, because RUNNER_TEMP is not shared with
|
||||
// Docker-based actions on the filesystem. Exporting to GITHUB_WORKSPACE
|
||||
// ensures that the exported credentials are automatically available to
|
||||
// Docker-based actions without user modification.
|
||||
//
|
||||
// This has the unintended side-effect of leaking credentials over time,
|
||||
// because GITHUB_WORKSPACE is not automatically cleaned up on self-hosted
|
||||
// runners. To mitigate this issue, this action defines a post step to
|
||||
// remove any created credentials.
|
||||
const githubWorkspace = process.env.GITHUB_WORKSPACE;
|
||||
if (!githubWorkspace) {
|
||||
throw new Error('$GITHUB_WORKSPACE is not set');
|
||||
}
|
||||
|
||||
// There have been a number of issues where users have not used the
|
||||
// "actions/checkout" step before our action. Our action relies on the
|
||||
// creation of that directory; worse, if a user puts "actions/checkout"
|
||||
// after our action, it will delete the exported credential. This
|
||||
// following code does a small check to see if there are any files in the
|
||||
// directory. It emits a warning if there are no files, since there may be
|
||||
// legitimate use cases for authenticating without checking out the
|
||||
// repository.
|
||||
const githubWorkspaceIsEmpty = await isEmptyDir(githubWorkspace);
|
||||
if (githubWorkspaceIsEmpty) {
|
||||
logger.warning(
|
||||
`The "create_credentials_file" option is true, but the current ` +
|
||||
`GitHub workspace is empty. Did you forget to use ` +
|
||||
`"actions/checkout" before this step? If you do not intend to ` +
|
||||
`share authentication with future steps in this job, set ` +
|
||||
`"create_credentials_file" to false.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Create credentials file.
|
||||
const outputFile = generateCredentialsFilename();
|
||||
const outputPath = pathjoin(githubWorkspace, outputFile);
|
||||
const credentialsPath = await client.createCredentialsFile(outputPath);
|
||||
logger.info(`Created credentials file at "${credentialsPath}"`);
|
||||
|
||||
// Output to be available to future steps.
|
||||
setOutput('credentials_file_path', credentialsPath);
|
||||
|
||||
if (exportEnvironmentVariables) {
|
||||
// CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE is picked up by gcloud to
|
||||
// use a specific credential file (subject to change and equivalent to
|
||||
// auth/credential_file_override).
|
||||
exportVariable('CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE', credentialsPath);
|
||||
|
||||
// GOOGLE_APPLICATION_CREDENTIALS is used by Application Default
|
||||
// Credentials in all GCP client libraries.
|
||||
exportVariable('GOOGLE_APPLICATION_CREDENTIALS', credentialsPath);
|
||||
|
||||
// GOOGLE_GHA_CREDS_PATH is used by other Google GitHub Actions.
|
||||
exportVariable('GOOGLE_GHA_CREDS_PATH', credentialsPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the project ID environment variables to the computed values.
|
||||
if (!projectID) {
|
||||
logger.warning(
|
||||
`Unable to compute project ID from inputs, skipping export. Please ` +
|
||||
`specify the "project_id" input directly.`,
|
||||
);
|
||||
} else {
|
||||
setOutput('project_id', projectID);
|
||||
|
||||
if (exportEnvironmentVariables) {
|
||||
exportVariable('CLOUDSDK_CORE_PROJECT', projectID);
|
||||
exportVariable('CLOUDSDK_PROJECT', projectID);
|
||||
exportVariable('GCLOUD_PROJECT', projectID);
|
||||
exportVariable('GCP_PROJECT', projectID);
|
||||
exportVariable('GOOGLE_CLOUD_PROJECT', projectID);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to generate a token. This will ensure the action correctly errors
|
||||
// if the credentials are misconfigured. This is also required so the value
|
||||
// can be set as an output for future authentication calls.
|
||||
const authToken = await client.getToken();
|
||||
logger.debug(`Successfully generated auth token`);
|
||||
setSecret(authToken);
|
||||
setOutput('auth_token', authToken);
|
||||
|
||||
// Create the credential client, we might not use it, but it's basically free.
|
||||
const iamCredentialsClient = new IAMCredentialsClient({
|
||||
logger: logger,
|
||||
universe: universe,
|
||||
|
||||
authToken: authToken,
|
||||
});
|
||||
|
||||
switch (tokenFormat) {
|
||||
case '': {
|
||||
break;
|
||||
}
|
||||
case null: {
|
||||
break;
|
||||
}
|
||||
case 'access_token': {
|
||||
logger.debug(`Creating access token`);
|
||||
|
||||
const accessTokenLifetime = parseDuration(getInput('access_token_lifetime'));
|
||||
const accessTokenScopes = parseMultilineCSV(getInput('access_token_scopes'));
|
||||
const accessTokenSubject = getInput('access_token_subject');
|
||||
|
||||
// Ensure a service_account was provided if using WIF.
|
||||
if (!serviceAccount) {
|
||||
throw new Error(
|
||||
'The GitHub Action workflow must specify a "service_account" to ' +
|
||||
'use when generating an OAuth 2.0 Access Token. ' +
|
||||
secretsWarning,
|
||||
);
|
||||
}
|
||||
|
||||
// If a subject was provided, use the traditional OAuth 2.0 flow to
|
||||
// perform Domain-Wide Delegation. Otherwise, use the modern IAM
|
||||
// Credentials endpoints.
|
||||
let accessToken;
|
||||
if (accessTokenSubject) {
|
||||
if (accessTokenLifetime > 3600) {
|
||||
logger.info(
|
||||
`An access token subject was specified, triggering Domain-Wide ` +
|
||||
`Delegation flow. This flow does not support specifying an ` +
|
||||
`access token lifetime of greater than 1 hour.`,
|
||||
);
|
||||
}
|
||||
|
||||
const unsignedJWT = buildDomainWideDelegationJWT(
|
||||
serviceAccount,
|
||||
accessTokenSubject,
|
||||
accessTokenScopes,
|
||||
accessTokenLifetime,
|
||||
);
|
||||
const signedJWT = await client.signJWT(unsignedJWT);
|
||||
|
||||
accessToken = await iamCredentialsClient.generateDomainWideDelegationAccessToken(signedJWT);
|
||||
} else {
|
||||
accessToken = await iamCredentialsClient.generateAccessToken({
|
||||
serviceAccount,
|
||||
delegates,
|
||||
scopes: accessTokenScopes,
|
||||
lifetime: accessTokenLifetime,
|
||||
});
|
||||
}
|
||||
|
||||
setSecret(accessToken);
|
||||
setOutput('access_token', accessToken);
|
||||
break;
|
||||
}
|
||||
case 'id_token': {
|
||||
logger.debug(`Creating id token`);
|
||||
|
||||
const idTokenAudience = getInput('id_token_audience', { required: true });
|
||||
const idTokenIncludeEmail = getBooleanInput('id_token_include_email');
|
||||
|
||||
// Ensure a service_account was provided if using WIF.
|
||||
if (!serviceAccount) {
|
||||
throw new Error(
|
||||
'The GitHub Action workflow must specify a "service_account" to ' +
|
||||
'use when generating an OAuth 2.0 Access Token. ' +
|
||||
secretsWarning,
|
||||
);
|
||||
}
|
||||
|
||||
const idToken = await iamCredentialsClient.generateIDToken({
|
||||
serviceAccount,
|
||||
audience: idTokenAudience,
|
||||
delegates,
|
||||
includeEmail: idTokenIncludeEmail,
|
||||
});
|
||||
setSecret(idToken);
|
||||
setOutput('id_token', idToken);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown token format "${tokenFormat}"`);
|
||||
}
|
||||
}
|
||||
if (require.main === module) {
|
||||
run(new Logger());
|
||||
}
|
||||
|
||||
run();
|
||||
|
63
src/post.ts
63
src/post.ts
@ -18,46 +18,39 @@ import { errorMessage, forceRemove } from '@google-github-actions/actions-utils'
|
||||
|
||||
import { Logger } from './logger';
|
||||
|
||||
/**
|
||||
* Executes the post action, documented inline.
|
||||
*/
|
||||
export async function run() {
|
||||
const logger = new Logger();
|
||||
|
||||
export async function run(logger: Logger) {
|
||||
try {
|
||||
main(logger);
|
||||
const createCredentials = getBooleanInput('create_credentials_file');
|
||||
if (!createCredentials) {
|
||||
logger.info(`Skipping credential cleanup - "create_credentials_file" is false.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanupCredentials = getBooleanInput('cleanup_credentials');
|
||||
if (!cleanupCredentials) {
|
||||
logger.info(`Skipping credential cleanup - "cleanup_credentials" is false.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// another environment variable manually.
|
||||
const credentialsPath = process.env['GOOGLE_GHA_CREDS_PATH'];
|
||||
if (!credentialsPath) {
|
||||
logger.info(`Skipping credential cleanup - $GOOGLE_GHA_CREDS_PATH is not set.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the file.
|
||||
await forceRemove(credentialsPath);
|
||||
logger.info(`Removed exported credentials at "${credentialsPath}".`);
|
||||
} catch (err) {
|
||||
const msg = errorMessage(err);
|
||||
setFailed(`google-github-actions/auth post failed with: ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(logger: Logger) {
|
||||
const createCredentials = getBooleanInput('create_credentials_file');
|
||||
if (!createCredentials) {
|
||||
logger.info(`Skipping credential cleanup - "create_credentials_file" is false.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanupCredentials = getBooleanInput('cleanup_credentials');
|
||||
if (!cleanupCredentials) {
|
||||
logger.info(`Skipping credential cleanup - "cleanup_credentials" is false.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// another environment variable manually.
|
||||
const credentialsPath = process.env['GOOGLE_GHA_CREDS_PATH'];
|
||||
if (!credentialsPath) {
|
||||
logger.info(`Skipping credential cleanup - $GOOGLE_GHA_CREDS_PATH is not set.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the file.
|
||||
await forceRemove(credentialsPath);
|
||||
logger.info(`Removed exported credentials at "${credentialsPath}".`);
|
||||
if (require.main === module) {
|
||||
run(new Logger());
|
||||
}
|
||||
|
||||
run();
|
||||
|
Loading…
Reference in New Issue
Block a user