From f698871603f6fb7dce5378dad8a0da58d52213a5 Mon Sep 17 00:00:00 2001 From: Tom Keller Date: Thu, 27 Oct 2022 21:16:31 -0700 Subject: [PATCH] chore!: set linters to maximum strictness --- .eslintignore | 2 + .eslintrc.yml | 141 +++++++++++++++++++++++++++++++++---------- package.json | 4 +- src/assumeRole.ts | 17 +++--- src/cleanup/index.ts | 10 +-- src/helpers.ts | 6 +- src/index.ts | 22 ++++--- test/cleanup.test.ts | 8 +-- test/helpers.test.ts | 4 +- test/index.test.ts | 84 +++++++++++++------------- tsconfig.build.json | 9 +++ tsconfig.json | 3 +- 12 files changed, 202 insertions(+), 108 deletions(-) create mode 100644 .eslintignore create mode 100644 tsconfig.build.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..9d0b71a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +build +dist diff --git a/.eslintrc.yml b/.eslintrc.yml index f516d35..223d05f 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -11,11 +11,8 @@ parserOptions: extends: - plugin:prettier/recommended - prettier - - plugin:@typescript-eslint/recommended - - plugin:import/typescript rules: - prettier/prettier: - - error + prettier/prettier: [error] import/no-extraneous-dependencies: - error - devDependencies: @@ -23,8 +20,7 @@ rules: - "**/build-tools/**" optionalDependencies: false peerDependencies: true - import/no-unresolved: - - error + import/no-unresolved: [error] import/order: - warn - groups: @@ -33,22 +29,62 @@ rules: alphabetize: order: asc caseInsensitive: true - no-duplicate-imports: - - error - no-shadow: - - off - key-spacing: - - error - no-multiple-empty-lines: - - error - no-return-await: - - off - no-trailing-spaces: - - error - dot-notation: - - error - no-bitwise: + array-callback-return: [warn] + no-await-in-loop: [warn] + no-constant-binary-expression: [error] + no-constructor-return: [error] + no-duplicate-imports: [error] + no-self-compare: [warn] + no-template-curly-in-string: [error] + no-unmodified-loop-condition: [error] + no-unreachable-loop: [error] + no-unused-private-class-members: [error] + no-use-before-define: [error] + require-atomic-updates: [error] + block-scoped-var: [warn] + camelcase: [warn] + class-methods-use-this: [error] + consistent-return: [warn] + consistent-this: [warn] + default-case-last: [warn] + default-param-last: [warn] + dot-notation: [error] + eqeqeq: [error] + guard-for-in: [warn] + logical-assignment-operators: - error + - always + - enforceForIfStatements: false + no-array-constructor: [error] + no-bitwise: [error] + no-console: [warn] + no-empty-function: [warn] + no-eval: [error] + no-extra-bind: [error] + no-labels: [error] + no-implicit-globals: [error] + no-invalid-this: [error] + key-spacing: [error] + no-multiple-empty-lines: [error] + no-return-await: [warn] + no-trailing-spaces: [error] + no-lonely-if: [error] + no-nested-ternary: [warn] + no-mixed-operators: [warn] + no-proto: [error] + no-sequences: [error] + no-throw-literal: [error] + no-useless-call: [error] + no-useless-concat: [warn] + no-var: [error] + one-var-declaration-per-line: [error] + prefer-const: [warn] + prefer-arrow-callback: [warn] + prefer-regex-literals: [warn] + prefer-promise-reject-errors: [warn] + prefer-spread: [warn] + prefer-template: [warn] + require-await: [error] overrides: - files: - '**/*.ts' @@ -59,24 +95,63 @@ overrides: project: ./tsconfig.json extends: - plugin:@typescript-eslint/recommended + - plugin:@typescript-eslint/recommended-requiring-type-checking + - plugin:import/typescript rules: - no-unused-vars: - - off + '@typescript-eslint/array-type': + - warn + - default: array-simple + '@typescript-eslint/ban-tslint-comment': [error] + '@typescript-eslint/consistent-indexed-object-style': [warn] + '@typescript-eslint/consistent-type-assertions': [warn] + '@typescript-eslint/prefer-includes': [warn] + dot-notation: [off] + '@typescript-eslint/dot-notation': [error] + '@typescript-eslint/consistent-type-exports': [warn] + '@typescript-eslint/consistent-type-imports': [warn] + '@typescript-eslint/explicit-member-accessibility': [warn] + '@typescript-eslint/no-base-to-string': [error] + '@typescript-eslint/no-confusing-non-null-assertion': [warn] + '@typescript-eslint/no-invalid-void-type': [error] + '@typescript-eslint/no-meaningless-void-operator': [warn] + '@typescript-eslint/no-redundant-type-constituents': [warn] + '@typescript-eslint/no-unnecessary-boolean-literal-compare': [warn] + '@typescript-eslint/no-unnecessary-condition': [warn] + '@typescript-eslint/no-unnecessary-qualifier': [warn] + '@typescript-eslint/no-unnecessary-type-arguments': [warn] + '@typescript-eslint/non-nullable-type-assertion-style': [warn] + '@typescript-eslint/prefer-for-of': [error] + '@typescript-eslint/prefer-literal-enum-member': [warn] + '@typescript-eslint/prefer-nullish-coalescing': [warn] + '@typescript-eslint/prefer-optional-chain': [warn] + '@typescript-eslint/prefer-readonly': [warn] + '@typescript-eslint/prefer-regexp-exec': [warn] + '@typescript-eslint/prefer-string-starts-ends-with': [warn] + '@typescript-eslint/prefer-ts-expect-error': [error] + '@typescript-eslint/promise-function-async': [warn] + '@typescript-eslint/require-array-sort-compare': [error] + default-param-last: [off] + '@typescript-eslint/default-param-last': [warn] + no-array-constructor: [off] + '@typescript-eslint/no-array-constructor': [error] + no-dupe-class-members: [off] + '@typescript-eslint/no-dupe-class-members': [warn] + no-invalid-this: [off] + '@typescript-eslint/no-invalid-this': [warn] + no-unused-vars: [off] '@typescript-eslint/no-unused-vars': - error - varsIgnorePattern: '^_' argsIgnorePattern: '^_' caughtErrorsIgnorePattern: '^_' - '@typescript-eslint/no-non-null-assertion': - - off + '@typescript-eslint/no-non-null-assertion': [off] '@typescript-eslint/no-require-imports': - error - "@typescript-eslint/return-await": - - error - "@typescript-eslint/no-shadow": - - error - "@typescript-eslint/no-floating-promises": - - error + no-return-await: [off] + '@typescript-eslint/return-await': [error] + no-shadow: [off] + '@typescript-eslint/no-shadow': [error] + '@typescript-eslint/no-floating-promises': [error] "@typescript-eslint/member-ordering": - error - default: @@ -89,4 +164,6 @@ overrides: - field - constructor - method - + no-use-before-define: [off] + '@typescript-eslint/no-use-before-define': [error] + no-duplicate-imports: [off] diff --git a/package.json b/package.json index 8022940..917bba8 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "configure-aws-credentials", "description": "A GitHub Action to configure AWS credentials", "scripts": { - "build": "tsc", + "build": "tsc --project tsconfig.build.json", "lint": "eslint .", - "package": "ncc build --license ../THIRD-PARTY -o dist", + "package": "npm run build && ncc build --license ../THIRD-PARTY -o dist", "test": "npm run lint && jest --verbose" }, "author": { diff --git a/src/assumeRole.ts b/src/assumeRole.ts index e722c78..0bb12d4 100644 --- a/src/assumeRole.ts +++ b/src/assumeRole.ts @@ -2,7 +2,8 @@ import assert from 'assert'; import fs from 'fs'; import path from 'path'; import * as core from '@actions/core'; -import { AssumeRoleCommand, AssumeRoleCommandInput, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts'; +import type { AssumeRoleCommandInput, Tag } from '@aws-sdk/client-sts'; +import { AssumeRoleCommand, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts'; import { errorMessage, getStsClient, isDefined } from './helpers'; const SANITIZATION_CHARACTER = '_'; @@ -64,7 +65,7 @@ export async function assumeRole(params: assumeRoleParams) { RoleArn = `arn:aws:iam::${sourceAccountId}:role/${RoleArn}`; } - const tagArray = [ + const tagArray: Tag[] = [ { Key: 'GitHub', Value: 'Actions' }, { Key: 'Repository', Value: GITHUB_REPOSITORY }, { Key: 'Workflow', Value: sanitizeGithubWorkflowName(GITHUB_WORKFLOW) }, @@ -73,23 +74,25 @@ export async function assumeRole(params: assumeRoleParams) { { Key: 'Commit', Value: GITHUB_SHA }, ]; - if (process.env.GITHUB_REF) { - tagArray.push({ Key: 'Branch', Value: process.env.GITHUB_REF }); + if (process.env['GITHUB_REF']) { + tagArray.push({ Key: 'Branch', Value: process.env['GITHUB_REF'] }); } const Tags = roleSkipSessionTagging ? undefined : tagArray; if (!Tags) { core.debug('Role session tagging has been skipped.'); } else { - core.debug(Tags.length + ' role session tags are being used.'); + core.debug(`${Tags.length} role session tags are being used.`); } + const ExternalId = roleExternalId; + const commonAssumeRoleParams: AssumeRoleCommandInput = { RoleArn, RoleSessionName: roleSessionName, DurationSeconds: roleDurationSeconds, - Tags, - ExternalId: roleExternalId, + ...(Tags ? { Tags } : {}), + ...(ExternalId ? { ExternalId } : {}), }; const keys = Object.keys(commonAssumeRoleParams) as Array; keys.forEach((k) => commonAssumeRoleParams[k] === undefined && delete commonAssumeRoleParams[k]); diff --git a/src/cleanup/index.ts b/src/cleanup/index.ts index 26ddf66..60db8e1 100644 --- a/src/cleanup/index.ts +++ b/src/cleanup/index.ts @@ -12,7 +12,7 @@ import { errorMessage } from '../helpers'; * with any other jobs. */ -export async function cleanup() { +export function cleanup() { try { // The GitHub Actions toolkit does not have an option to completely unset // environment variables, so we overwrite the current value with an empty @@ -29,9 +29,9 @@ export async function cleanup() { } /* c8 ignore start */ if (require.main === module) { - (async () => { - await cleanup(); - })().catch((error) => { + try { + cleanup(); + } catch (error) { core.setFailed(errorMessage(error)); - }); + } } diff --git a/src/helpers.ts b/src/helpers.ts index 9526e04..958532a 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -5,11 +5,11 @@ const SANITIZATION_CHARACTER = '_'; let stsclient: STSClient | undefined; -export function getStsClient(region: string, agent?: string) { +export function getStsClient(region: string, customUserAgent?: string) { if (!stsclient) { stsclient = new STSClient({ region, - customUserAgent: agent, + ...(customUserAgent ? { customUserAgent } : {}), }); } return stsclient; @@ -39,7 +39,7 @@ export function isDefined(i: T | undefined | null): i is T { } /* c8 ignore stop */ -export function defaultSleep(ms: number) { +export async function defaultSleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } let sleep = defaultSleep; diff --git a/src/index.ts b/src/index.ts index ed1c73b..3cc9f9d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import * as core from '@actions/core'; -import { Credentials, GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts'; +import type { Credentials } from '@aws-sdk/client-sts'; +import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts'; import { assumeRole } from './assumeRole'; import { errorMessage, getStsClient, retryAndBackoff } from './helpers'; @@ -35,7 +36,7 @@ function exportCredentials(creds?: Partial) { if (creds?.SessionToken) { core.setSecret(creds.SessionToken); core.exportVariable('AWS_SESSION_TOKEN', creds.SessionToken); - } else if (process.env.AWS_SESSION_TOKEN) { + } else if (process.env['AWS_SESSION_TOKEN']) { // clear session token from previous credentials action core.exportVariable('AWS_SESSION_TOKEN', ''); } @@ -104,7 +105,8 @@ export async function run() { const audience = core.getInput('audience', { required: false }); const SecretAccessKey = core.getInput('aws-secret-access-key', { required: false }); const region = core.getInput('aws-region', { required: true }); - const SessionToken = core.getInput('aws-session-token', { required: false }); + const sessionTokenInput = core.getInput('aws-session-token', { required: false }); + const SessionToken = sessionTokenInput === '' ? undefined : sessionTokenInput; const maskAccountId = (core.getInput('mask-aws-account-id', { required: false }) || 'true').toLowerCase() === 'true'; const roleToAssume = core.getInput('role-to-assume', { required: false }); @@ -113,11 +115,11 @@ export async function run() { // This wraps the logic for deciding if we should rely on the GH OIDC provider since we may need to reference // the decision in a few differennt places. Consolidating it here makes the logic clearer elsewhere. const useGitHubOIDCProvider = - !!roleToAssume && !!process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN && !AccessKeyId && !webIdentityTokenFile; + !!roleToAssume && !!process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] && !AccessKeyId && !webIdentityTokenFile; const roleDurationSeconds = - parseInt(core.getInput('role-duration-seconds', { required: false })) || - (SessionToken && SESSION_ROLE_DURATION) || - (useGitHubOIDCProvider && DEFAULT_ROLE_DURATION_FOR_OIDC_ROLES) || + (parseInt(core.getInput('role-duration-seconds', { required: false })) || + ((SessionToken ? SESSION_ROLE_DURATION : undefined) ?? + (useGitHubOIDCProvider ? DEFAULT_ROLE_DURATION_FOR_OIDC_ROLES : undefined))) ?? MAX_ACTION_RUNTIME; const roleSessionName = core.getInput('role-session-name', { required: false }) || ROLE_SESSION_NAME; const roleSkipSessionTaggingInput = core.getInput('role-skip-session-tagging', { required: false }) || 'false'; @@ -181,7 +183,7 @@ export async function run() { // First: self-hosted runners. If the GITHUB_ACTIONS environment variable // is set to `true` then we are NOT in a self-hosted runner. // Second: Customer provided credentials manually (IAM User keys stored in GH Secrets) - if (!process.env.GITHUB_ACTIONS || AccessKeyId) { + if (!process.env['GITHUB_ACTIONS'] || AccessKeyId) { await validateCredentials(roleCredentials.Credentials?.AccessKeyId); } await exportAccountId(region, maskAccountId); @@ -189,7 +191,7 @@ export async function run() { } catch (error) { core.setFailed(errorMessage(error)); - const showStackTrace = process.env.SHOW_STACK_TRACE; + const showStackTrace = process.env['SHOW_STACK_TRACE']; if (showStackTrace === 'true') { throw error; @@ -202,6 +204,6 @@ if (require.main === module) { (async () => { await run(); })().catch((error) => { - core.setFailed(error.message); + core.setFailed(errorMessage(error)); }); } diff --git a/test/cleanup.test.ts b/test/cleanup.test.ts index 0c92f27..3478223 100644 --- a/test/cleanup.test.ts +++ b/test/cleanup.test.ts @@ -29,8 +29,8 @@ describe('Configure AWS Credentials', () => { process.env = OLD_ENV; }); - test('replaces AWS credential and region env vars with empty strings', async () => { - await cleanup(); + test('replaces AWS credential and region env vars with empty strings', () => { + cleanup(); expect(core.setFailed).toHaveBeenCalledTimes(0); expect(core.exportVariable).toHaveBeenCalledTimes(5); expect(core.exportVariable).toHaveBeenCalledWith('AWS_ACCESS_KEY_ID', ''); @@ -40,12 +40,12 @@ describe('Configure AWS Credentials', () => { expect(core.exportVariable).toHaveBeenCalledWith('AWS_REGION', ''); }); - test('error is caught and fails the action', async () => { + test('error is caught and fails the action', () => { jest.spyOn(core, 'exportVariable').mockImplementation(() => { throw new Error(); }); - await cleanup(); + cleanup(); expect(core.setFailed).toHaveBeenCalled(); }); diff --git a/test/helpers.test.ts b/test/helpers.test.ts index 452d0a6..ecd19b2 100644 --- a/test/helpers.test.ts +++ b/test/helpers.test.ts @@ -13,9 +13,9 @@ describe('helpers', () => { expect(helpers.sanitizeGithubWorkflowName('sdf234@#$%$^&*()_+{}|:"<>?')).toEqual('sdf234@__________+___:_<>?'); }); - test('can sleep', async () => { + test('can sleep', () => { const sleep = helpers.defaultSleep(10); - await expect(Promise.race([sleep, new Promise((_res, rej) => setTimeout(rej, 20))])).resolves; + expect(Promise.race([sleep, new Promise((_res, rej) => setTimeout(rej, 20))])).resolves; }); test("backoff function doesn't retry non-retryable errors", async () => { diff --git a/test/index.test.ts b/test/index.test.ts index 4597da4..f079b03 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -49,11 +49,12 @@ const ASSUME_ROLE_INPUTS = { ...CREDS_INPUTS, 'role-to-assume': ROLE_ARN, 'aws-r const mockedSTS = mockClient(STSClient); function mockGetInput(requestResponse: Record) { - return function (name: string, _options: unknown) { - return requestResponse[name]; + return function (name: string, _options: unknown): string { + return requestResponse[name]!; }; } +// eslint-disable-next-line @typescript-eslint/no-unsafe-return jest.mock('fs', () => ({ ...jest.requireActual('fs'), existsSync: jest.fn(() => true), @@ -61,11 +62,11 @@ jest.mock('fs', () => ({ })); jest.mock('@aws-sdk/credential-provider-env', () => ({ // This is the actual implementation in the SDK ^_^ - fromEnv: jest.fn().mockImplementation(() => async () => { - const accessKeyId = process.env.AWS_ACCESS_KEY_ID; - const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; - const sessionToken = process.env.AWS_SESSION_TOKEN; - const expiration = process.env.AWS_CREDENTIAL_EXPIRATION; + fromEnv: jest.fn().mockImplementation(() => () => { + const accessKeyId = process.env['AWS_ACCESS_KEY_ID']; + const secretAccessKey = process.env['AWS_SECRET_ACCESS_KEY']; + const sessionToken = process.env['AWS_SESSION_TOKEN']; + const expiration = process.env['AWS_CREDENTIAL_EXPIRATION']; return { accessKeyId, secretAccessKey, @@ -84,18 +85,18 @@ describe('Configure AWS Credentials', () => { jest.clearAllMocks(); mockedSTS.reset(); (fromEnv as jest.Mock).mockReset(); - jest.spyOn(core, 'getIDToken').mockImplementation(() => Promise.resolve('testtoken')); + jest.spyOn(core, 'getIDToken').mockImplementation(async () => Promise.resolve('testtoken')); jest.spyOn(core, 'exportVariable').mockImplementation(); jest.spyOn(core, 'setSecret').mockImplementation(); jest.spyOn(core, 'setOutput').mockImplementation(); jest.spyOn(core, 'setFailed').mockImplementation(); jest.spyOn(core, 'debug').mockImplementation(); (fromEnv as jest.Mock) - .mockImplementationOnce(() => async () => ({ + .mockImplementationOnce(() => () => ({ accessKeyId: FAKE_ACCESS_KEY_ID, secretAccessKey: FAKE_SECRET_ACCESS_KEY, })) - .mockImplementationOnce(() => async () => ({ + .mockImplementationOnce(() => () => ({ accessKeyId: FAKE_STS_ACCESS_KEY_ID, secretAccessKey: FAKE_STS_SECRET_ACCESS_KEY, })); @@ -119,7 +120,7 @@ describe('Configure AWS Credentials', () => { Expiration: new Date(8640000000000000), }, }); - withsleep(() => { + withsleep(async () => { return Promise.resolve(); }); }); @@ -151,7 +152,7 @@ describe('Configure AWS Credentials', () => { test('action fails when github env vars are not set', async () => { jest.spyOn(core, 'getInput').mockImplementation(mockGetInput(ASSUME_ROLE_INPUTS)); - delete process.env.GITHUB_SHA; + delete process.env['GITHUB_SHA']; await run(); @@ -162,7 +163,7 @@ describe('Configure AWS Credentials', () => { test('action does not require GITHUB_REF env var', async () => { jest.spyOn(core, 'getInput').mockImplementation(mockGetInput(DEFAULT_INPUTS)); - delete process.env.GITHUB_REF; + delete process.env['GITHUB_REF']; await run(); @@ -188,7 +189,7 @@ describe('Configure AWS Credentials', () => { const mockInputs = { 'aws-region': FAKE_REGION }; jest.spyOn(core, 'getInput').mockImplementation(mockGetInput(mockInputs)); (fromEnv as jest.Mock).mockReset(); - (fromEnv as jest.Mock).mockImplementation(() => async () => { + (fromEnv as jest.Mock).mockImplementation(() => () => { throw new CredentialsProviderError('test'); }); @@ -248,9 +249,9 @@ describe('Configure AWS Credentials', () => { test('existing env var creds are cleared', async () => { const mockInputs = { ...CREDS_INPUTS, 'aws-region': 'eu-west-1' }; jest.spyOn(core, 'getInput').mockImplementation(mockGetInput(mockInputs)); - process.env.AWS_ACCESS_KEY_ID = 'foo'; - process.env.AWS_SECRET_ACCESS_KEY = 'bar'; - process.env.AWS_SESSION_TOKEN = 'helloworld'; + process.env['AWS_ACCESS_KEY_ID'] = 'foo'; + process.env['AWS_SECRET_ACCESS_KEY'] = 'bar'; + process.env['AWS_SESSION_TOKEN'] = 'helloworld'; await run(); @@ -389,7 +390,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 6 * 3600, @@ -411,7 +412,7 @@ describe('Configure AWS Credentials', () => { .mockImplementation(mockGetInput({ ...ASSUME_ROLE_INPUTS, 'role-duration-seconds': '5' })); await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 5, @@ -433,7 +434,7 @@ describe('Configure AWS Credentials', () => { .mockImplementation(mockGetInput({ ...ASSUME_ROLE_INPUTS, 'role-session-name': 'MySessionName' })); await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'MySessionName', DurationSeconds: 6 * 3600, @@ -455,7 +456,7 @@ describe('Configure AWS Credentials', () => { .mockImplementation(mockGetInput({ ...ASSUME_ROLE_INPUTS, 'aws-session-token': FAKE_SESSION_TOKEN })); await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 3600, @@ -475,7 +476,7 @@ describe('Configure AWS Credentials', () => { jest.spyOn(core, 'getInput').mockImplementation(mockGetInput({ ...ASSUME_ROLE_INPUTS })); await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 6 * 3600, @@ -497,7 +498,7 @@ describe('Configure AWS Credentials', () => { .mockImplementation(mockGetInput({ ...CREDS_INPUTS, 'role-to-assume': ROLE_NAME, 'aws-region': FAKE_REGION })); await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: 'arn:aws:iam::123456789012:role/MY-ROLE', RoleSessionName: 'GitHubActions', DurationSeconds: 6 * 3600, @@ -524,7 +525,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0]?.args[0].input).toEqual({ RoleArn: 'arn:aws:iam::111111111111:role/MY-ROLE', RoleSessionName: 'GitHubActions', DurationSeconds: 6 * 3600, @@ -543,7 +544,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0]?.args[0].input).toEqual({ RoleArn: 'arn:aws:iam::111111111111:role/MY-ROLE', RoleSessionName: 'GitHubActions', DurationSeconds: 6 * 3600, @@ -552,8 +553,8 @@ describe('Configure AWS Credentials', () => { }); test('only role arn and region provided to use GH OIDC Token', async () => { - process.env.GITHUB_ACTIONS = 'true'; - process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token'; + process.env['GITHUB_ACTIONS'] = 'true'; + process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] = 'test-token'; jest .spyOn(core, 'getInput') @@ -561,7 +562,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0]?.args[0].input).toEqual({ RoleArn: 'arn:aws:iam::111111111111:role/MY-ROLE', RoleSessionName: 'GitHubActions', DurationSeconds: 3600, @@ -574,8 +575,8 @@ describe('Configure AWS Credentials', () => { test('GH OIDC With custom role duration', async () => { const CUSTOM_ROLE_DURATION = '1234'; - process.env.GITHUB_ACTIONS = 'true'; - process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token'; + process.env['GITHUB_ACTIONS'] = 'true'; + process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] = 'test-token'; jest.spyOn(core, 'getInput').mockImplementation( mockGetInput({ 'role-to-assume': ROLE_ARN, @@ -586,7 +587,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleWithWebIdentityCommand)[0]?.args[0].input).toEqual({ RoleArn: 'arn:aws:iam::111111111111:role/MY-ROLE', RoleSessionName: 'GitHubActions', DurationSeconds: parseInt(CUSTOM_ROLE_DURATION), @@ -598,8 +599,8 @@ describe('Configure AWS Credentials', () => { }); test('role assumption fails after maximum trials using OIDC provider', async () => { - process.env.GITHUB_ACTIONS = 'true'; - process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'test-token'; + process.env['GITHUB_ACTIONS'] = 'true'; + process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] = 'test-token'; jest .spyOn(core, 'getInput') .mockImplementation(mockGetInput({ 'role-to-assume': ROLE_ARN, 'aws-region': FAKE_REGION })); @@ -618,7 +619,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 6 * 3600, @@ -649,7 +650,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 6 * 3600, @@ -672,7 +673,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 21600, @@ -687,7 +688,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 21600, @@ -708,7 +709,7 @@ describe('Configure AWS Credentials', () => { await run(); - expect(mockedSTS.commandCalls(AssumeRoleCommand)[0].args[0].input).toEqual({ + expect(mockedSTS.commandCalls(AssumeRoleCommand)[0]?.args[0].input).toEqual({ RoleArn: ROLE_ARN, RoleSessionName: 'GitHubActions', DurationSeconds: 21600, @@ -733,10 +734,11 @@ describe('Configure AWS Credentials', () => { maskedValues.push(secret); }); jest.spyOn(core, 'exportVariable').mockImplementation((name, value) => { - if (!maskedValues.includes(value) && !publicFields.includes(name)) { - throw new Error(value + ' for variable ' + name + ' is not masked yet!'); + const val = String(value); + if (!maskedValues.includes(val) && !publicFields.includes(name)) { + throw new Error(`{value} for variable ${name} is not masked yet!`); } - process.env[name] = value; + process.env[name] = val; }); await run(); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..8f01a41 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "test/**/*.ts" + ], + "compilerOptions": { + "rootDir": "src" + }, +} diff --git a/tsconfig.json b/tsconfig.json index 7dea6ac..2767279 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,9 +13,8 @@ "noUnusedParameters": true, "module": "CommonJS", "resolveJsonModule": true, - "rootDir": ".", "outDir": "build", - "composite": true, + "declaration": true, "newLine": "lf", "noEmitOnError": true, "sourceMap": true,