Compare commits

...

40 Commits

Author SHA1 Message Date
AWS SDKs and Tools bot 00943011d9 chore(main): release 5.1.0 (#1483)
Co-authored-by: Michael Lehmann <lehmanmj@amazon.com>
2025-10-06 10:22:34 -07:00
dependabot[bot] e381dc357a chore(deps-dev): bump @vercel/ncc from 0.38.3 to 0.38.4 (#1500)
Bumps [@vercel/ncc](https://github.com/vercel/ncc) from 0.38.3 to 0.38.4.
- [Release notes](https://github.com/vercel/ncc/releases)
- [Commits](https://github.com/vercel/ncc/compare/0.38.3...0.38.4)

---
updated-dependencies:
- dependency-name: "@vercel/ncc"
  dependency-version: 0.38.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Lehmann <lehmanmj@amazon.com>
2025-10-06 17:19:17 +00:00
dependabot[bot] 031a65bdc1 chore(deps-dev): bump del-cli from 6.0.0 to 7.0.0 (#1497)
Bumps [del-cli](https://github.com/sindresorhus/del-cli) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/sindresorhus/del-cli/releases)
- [Commits](https://github.com/sindresorhus/del-cli/compare/v6.0.0...v7.0.0)

---
updated-dependencies:
- dependency-name: del-cli
  dependency-version: 7.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Lehmann <lehmanmj@amazon.com>
2025-10-06 17:14:33 +00:00
GitHub Actions 9efe715a4b chore: Update dist 2025-10-06 17:03:50 +00:00
dependabot[bot] 6bbafa679f chore(deps-dev): bump @aws-sdk/credential-provider-env (#1502)
Bumps [@aws-sdk/credential-provider-env](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/credential-provider-env) from 3.894.0 to 3.899.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/credential-provider-env/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.899.0/packages/credential-provider-env)

---
updated-dependencies:
- dependency-name: "@aws-sdk/credential-provider-env"
  dependency-version: 3.899.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 17:03:07 +00:00
dependabot[bot] 4c879b4d5d chore(deps-dev): bump @types/node from 24.5.0 to 24.6.0 (#1503)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.5.0 to 24.6.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Lehmann <lehmanmj@amazon.com>
2025-10-06 17:00:25 +00:00
dependabot[bot] fb52c697f2 chore(deps-dev): bump memfs from 4.39.0 to 4.47.0 (#1504)
Bumps [memfs](https://github.com/streamich/memfs) from 4.39.0 to 4.47.0.
- [Release notes](https://github.com/streamich/memfs/releases)
- [Changelog](https://github.com/streamich/memfs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/streamich/memfs/compare/v4.39.0...v4.47.0)

---
updated-dependencies:
- dependency-name: memfs
  dependency-version: 4.47.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Lehmann <lehmanmj@amazon.com>
2025-10-06 16:58:44 +00:00
GitHub Actions f226b0540e chore: Update dist 2025-09-30 03:14:04 +00:00
dependabot[bot] be2cf0c768 chore(deps): bump @aws-sdk/client-sts from 3.888.0 to 3.899.0 (#1501)
Bumps [@aws-sdk/client-sts](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-sts) from 3.888.0 to 3.899.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-sts/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.899.0/clients/client-sts)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-sts"
  dependency-version: 3.899.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 03:13:23 +00:00
GitHub Actions 0be22f94c3 chore: Update dist 2025-09-23 03:04:11 +00:00
dependabot[bot] 555a8e9ce6 chore(deps-dev): bump @aws-sdk/credential-provider-env (#1495)
Bumps [@aws-sdk/credential-provider-env](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/credential-provider-env) from 3.888.0 to 3.894.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/credential-provider-env/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.894.0/packages/credential-provider-env)

---
updated-dependencies:
- dependency-name: "@aws-sdk/credential-provider-env"
  dependency-version: 3.894.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-23 03:03:30 +00:00
GitHub Actions 6e562ac9ab chore: Update dist 2025-09-16 03:12:34 +00:00
dependabot[bot] 4283504248 chore(deps): bump @aws-sdk/client-sts from 3.883.0 to 3.888.0 (#1492)
Bumps [@aws-sdk/client-sts](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-sts) from 3.883.0 to 3.888.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-sts/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.888.0/clients/client-sts)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-sts"
  dependency-version: 3.888.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 03:11:52 +00:00
GitHub Actions 3146acf819 chore: Update dist 2025-09-16 03:10:19 +00:00
dependabot[bot] 8688f2d5c1 chore(deps-dev): bump @aws-sdk/credential-provider-env (#1490)
Bumps [@aws-sdk/credential-provider-env](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/credential-provider-env) from 3.883.0 to 3.888.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/credential-provider-env/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.888.0/packages/credential-provider-env)

---
updated-dependencies:
- dependency-name: "@aws-sdk/credential-provider-env"
  dependency-version: 3.888.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 03:09:38 +00:00
dependabot[bot] 56c08aa702 chore(deps-dev): bump @types/node from 24.3.1 to 24.5.0 (#1494)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.3.1 to 24.5.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 03:07:25 +00:00
dependabot[bot] eda31a964a chore(deps-dev): bump @biomejs/biome from 2.2.3 to 2.2.4 (#1493)
Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.2.3 to 2.2.4.
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.2.4/packages/@biomejs/biome)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.2.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 03:05:07 +00:00
dependabot[bot] 8b7cc57256 chore(deps-dev): bump memfs from 4.38.2 to 4.39.0 (#1491)
Bumps [memfs](https://github.com/streamich/memfs) from 4.38.2 to 4.39.0.
- [Release notes](https://github.com/streamich/memfs/releases)
- [Changelog](https://github.com/streamich/memfs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/streamich/memfs/compare/v4.38.2...v4.39.0)

---
updated-dependencies:
- dependency-name: memfs
  dependency-version: 4.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 03:03:05 +00:00
GitHub Actions 351d894493 chore: Update dist 2025-09-11 20:33:40 +00:00
Tom Keller 1584b8b0e2 feat: Add global timeout support (#1487) 2025-09-11 13:32:59 -07:00
GitHub Actions b845e0f4f4 chore: Update dist 2025-09-11 19:36:07 +00:00
Tom Keller cea42985ac fix: properly expose getProxyForUrl (introduced in #1482) (#1486)
* chore: use stricter typescript config

* fix: properly expose getProxyForUrl (in #1482)
2025-09-11 12:35:26 -07:00
GitHub Actions 6b19f7ffe6 chore: Update dist 2025-09-11 18:23:03 +00:00
Michał Pawlik 97ef425d73 feat: Improve debug logging in retry logic (#1485) 2025-09-11 11:22:18 -07:00
dependabot[bot] 6bf15574d0 chore(deps): bump vite from 6.3.5 to 7.1.5 (#1484)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.5 to 7.1.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-10 02:01:27 +00:00
GitHub Actions 3cb849cb01 chore: Update dist 2025-09-09 23:23:45 +00:00
Tom Keller dde9b22a8e feat: add no-proxy support (#1482) 2025-09-09 16:23:05 -07:00
dependabot[bot] 5ebd15afc6 chore(deps-dev): bump @biomejs/biome from 2.2.2 to 2.2.3 (#1480)
Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.2.3/packages/@biomejs/biome)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.2.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 03:10:22 +00:00
GitHub Actions c7a2280e36 chore: Update dist 2025-09-09 03:08:42 +00:00
dependabot[bot] 3de82418a6 chore(deps): bump @aws-sdk/client-sts from 3.873.0 to 3.883.0 (#1478)
Bumps [@aws-sdk/client-sts](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-sts) from 3.873.0 to 3.883.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-sts/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.883.0/clients/client-sts)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-sts"
  dependency-version: 3.883.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 03:08:05 +00:00
dependabot[bot] a3143e85cf chore(deps-dev): bump @aws-sdk/credential-provider-env (#1479)
Bumps [@aws-sdk/credential-provider-env](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/credential-provider-env) from 3.879.0 to 3.883.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/credential-provider-env/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.883.0/packages/credential-provider-env)

---
updated-dependencies:
- dependency-name: "@aws-sdk/credential-provider-env"
  dependency-version: 3.883.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 03:06:41 +00:00
GitHub Actions 3c23363139 chore: Update dist 2025-09-09 03:05:17 +00:00
dependabot[bot] 44e57d426c chore(deps): bump @smithy/node-http-handler from 4.1.1 to 4.2.0 (#1477)
Bumps [@smithy/node-http-handler](https://github.com/smithy-lang/smithy-typescript/tree/HEAD/packages/node-http-handler) from 4.1.1 to 4.2.0.
- [Release notes](https://github.com/smithy-lang/smithy-typescript/releases)
- [Changelog](https://github.com/smithy-lang/smithy-typescript/blob/main/packages/node-http-handler/CHANGELOG.md)
- [Commits](https://github.com/smithy-lang/smithy-typescript/commits/@smithy/node-http-handler@4.2.0/packages/node-http-handler)

---
updated-dependencies:
- dependency-name: "@smithy/node-http-handler"
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 03:04:26 +00:00
dependabot[bot] b876879204 chore(deps-dev): bump @types/node from 24.3.0 to 24.3.1 (#1475)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.3.0 to 24.3.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 03:02:29 +00:00
Henry Griffiths 8e2d02296b ci: update configure-aws-credentials to v5 (#1470)
BEGIN_COMMIT_OVERRIDE
chore: update configure-aws-credentials to v5 (#1470)
END_COMMIT_OVERRIDE
2025-09-04 16:58:13 -07:00
Henry Griffiths fb3078e4c8 chore: update readme version to v5 (#1469) 2025-09-04 16:56:03 -07:00
Henry Griffiths 0994a359d5 chore: update actions/checkout to v5 (#1471)
Co-authored-by: Tom Keller <1083460+kellertk@users.noreply.github.com>
2025-09-04 23:53:56 +00:00
Tom Keller 1ec058b7a1 chore: revert 163b496 (#1473) 2025-09-04 23:52:28 +00:00
Tom Keller 163b496f8b chore: update AWS role references to use variables (#1472) 2025-09-04 16:46:38 -07:00
Tom Keller f2e9d495b0 chore: remove release-please-action override (#1467) 2025-09-03 13:18:21 -07:00
27 changed files with 49807 additions and 17921 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v5
with:
aws-region: us-west-2
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
+2 -2
View File
@@ -15,10 +15,10 @@ jobs:
- name: Get Metadata
id: dependabot-metadata
uses: dependabot/fetch-metadata@v2
- uses: actions/checkout@v4
- uses: actions/checkout@v5
name: Clone repo
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v5
with:
aws-region: us-west-2
role-to-assume: ${{ secrets.CONFIGUREAWSCREDENTIALSPACKAGEROLEARN }}
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: ${{ github.ref_name }}
@@ -28,7 +28,7 @@ jobs:
npm test
npm run package
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v5
with:
aws-region: us-west-2
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
+2 -2
View File
@@ -15,12 +15,12 @@
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v5
with:
aws-region: us-west-2
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
timeout-minutes: 5
steps:
- name: "Checkout repostiory"
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: "Setup node"
uses: actions/setup-node@v4
with:
+1 -1
View File
@@ -1,5 +1,5 @@
{
".release-please-manifest.json": "4.0.2",
"package.json": "4.0.2",
".": "5.0.0"
".": "5.1.0"
}
+14
View File
@@ -2,6 +2,20 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [5.1.0](https://github.com/aws-actions/configure-aws-credentials/compare/v5.0.0...v5.1.0) (2025-10-06)
### Features
* Add global timeout support ([#1487](https://github.com/aws-actions/configure-aws-credentials/issues/1487)) ([1584b8b](https://github.com/aws-actions/configure-aws-credentials/commit/1584b8b0e2062557287c28fbe9b8920df434e866))
* add no-proxy support ([#1482](https://github.com/aws-actions/configure-aws-credentials/issues/1482)) ([dde9b22](https://github.com/aws-actions/configure-aws-credentials/commit/dde9b22a8e889a0821997a21a2c5a38020ee8de3))
* Improve debug logging in retry logic ([#1485](https://github.com/aws-actions/configure-aws-credentials/issues/1485)) ([97ef425](https://github.com/aws-actions/configure-aws-credentials/commit/97ef425d73aa532439f54f90d0e83101a186c5a6))
### Bug Fixes
* properly expose getProxyForUrl (introduced in [#1482](https://github.com/aws-actions/configure-aws-credentials/issues/1482)) ([#1486](https://github.com/aws-actions/configure-aws-credentials/issues/1486)) ([cea4298](https://github.com/aws-actions/configure-aws-credentials/commit/cea42985ac88b42678fbc84c18066a7f07f05176))
## [5.0.0](https://github.com/aws-actions/configure-aws-credentials/compare/v4.3.1...v5.0.0) (2025-09-03)
+13 -12
View File
@@ -152,6 +152,7 @@ See [action.yml](./action.yml) for more detail.
| use-existing-credentials | When set, the action will check if existing credentials are valid and exit if they are. Defaults to false. | No |
| allowed-account-ids | A comma-delimited list of expected AWS account IDs. The action will fail if we receive credentials for the wrong account. | No |
| force-skip-oidc | When set, the action will skip using GitHub OIDC provider even if the id-token permission is set. | No |
| action-timeout-s | Global timeout for the action in seconds. If set to a value greater than 0, the action will fail if it takes longer than this time to complete. | No |
</details>
#### Adjust the retry mechanism
@@ -180,7 +181,7 @@ this action will always consider the `HTTP_PROXY` environment variable.
Manually configured proxy:
```yaml
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
aws-region: us-east-2
role-to-assume: my-github-actions-role
@@ -251,13 +252,13 @@ line.
<summary>Inline session policy examples</summary>
```yaml
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
inline-session-policy: '{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:List*","Resource":"*"}]}'
```
Or we can have a nicely formatted JSON as well:
```yaml
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
inline-session-policy: >-
{
@@ -283,13 +284,13 @@ the role.
<summary>Managed session policy examples</summary>
```yaml
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
managed-session-policies: arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
```
And we can pass multiple managed policies likes this:
```yaml
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
managed-session-policies: |
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
@@ -327,7 +328,7 @@ You can specify the audience through the `audience` input:
```yaml
- name: Configure AWS Credentials for China region audience
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
audience: sts.amazonaws.com.cn
aws-region: cn-northwest-1
@@ -401,7 +402,7 @@ Examples
### AssumeRoleWithWebIdentity
```yaml
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
@@ -415,13 +416,13 @@ environment variable and use it to assume the role
### AssumeRole with role previously assumed by action in same workflow
```yaml
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
role-session-name: MySessionName
- name: Configure other AWS Credentials
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::987654321000:role/my-second-role
@@ -436,7 +437,7 @@ role, `arn:aws:iam::987654321000:role/my-second-role`.
### AssumeRole with static IAM credentials in repository secrets
```yaml
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -455,7 +456,7 @@ name, like `role-to-assume: my-github-actions-role`.
```yaml
- name: Configure AWS Credentials 1
id: creds
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
@@ -464,7 +465,7 @@ name, like `role-to-assume: my-github-actions-role`.
run: |
aws sts get-caller-identity
- name: Configure AWS Credentials 2
uses: aws-actions/configure-aws-credentials@v4.3.1
uses: aws-actions/configure-aws-credentials@v5.0.0
with:
aws-region: us-east-2
aws-access-key-id: ${{ steps.creds.outputs.aws-access-key-id }}
+6
View File
@@ -37,6 +37,9 @@ inputs:
http-proxy:
description: Proxy to use for the AWS SDK agent
required: false
no-proxy:
description: Hosts to skip for the proxy configuration
required: false
mask-aws-account-id:
description: Whether to mask the AWS account ID for these credentials as a secret value. By default the account ID will not be masked
required: false
@@ -86,6 +89,9 @@ inputs:
force-skip-oidc:
required: false
description: When enabled, this option will skip using GitHub OIDC provider even if the id-token permission is set. This is sometimes useful when using IAM instance credentials.
action-timeout-s:
required: false
description: A global timeout in seconds for the action. When the timeout is reached, the action immediately exits. The default is to run without a timeout.
outputs:
aws-account-id:
Generated Vendored
+224
View File
@@ -0,0 +1,224 @@
"use strict";
exports.id = 579;
exports.ids = [579];
exports.modules = {
/***/ 6579:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
var schema = __webpack_require__(6890);
var utilUtf8 = __webpack_require__(1577);
class EventStreamSerde {
marshaller;
serializer;
deserializer;
serdeContext;
defaultContentType;
constructor({ marshaller, serializer, deserializer, serdeContext, defaultContentType, }) {
this.marshaller = marshaller;
this.serializer = serializer;
this.deserializer = deserializer;
this.serdeContext = serdeContext;
this.defaultContentType = defaultContentType;
}
async serializeEventStream({ eventStream, requestSchema, initialRequest, }) {
const marshaller = this.marshaller;
const eventStreamMember = requestSchema.getEventStreamMember();
const unionSchema = requestSchema.getMemberSchema(eventStreamMember);
unionSchema.getMemberSchemas();
const serializer = this.serializer;
const defaultContentType = this.defaultContentType;
const initialRequestMarker = Symbol("initialRequestMarker");
const eventStreamIterable = {
async *[Symbol.asyncIterator]() {
if (initialRequest) {
const headers = {
":event-type": { type: "string", value: "initial-request" },
":message-type": { type: "string", value: "event" },
":content-type": { type: "string", value: defaultContentType },
};
serializer.write(requestSchema, initialRequest);
const body = serializer.flush();
yield {
[initialRequestMarker]: true,
headers,
body,
};
}
for await (const page of eventStream) {
yield page;
}
},
};
return marshaller.serialize(eventStreamIterable, (event) => {
if (event[initialRequestMarker]) {
return {
headers: event.headers,
body: event.body,
};
}
const unionMember = Object.keys(event).find((key) => {
return key !== "__type";
}) ?? "";
const { additionalHeaders, body, eventType, explicitPayloadContentType } = this.writeEventBody(unionMember, unionSchema, event);
const headers = {
":event-type": { type: "string", value: eventType },
":message-type": { type: "string", value: "event" },
":content-type": { type: "string", value: explicitPayloadContentType ?? defaultContentType },
...additionalHeaders,
};
return {
headers,
body,
};
});
}
async deserializeEventStream({ response, responseSchema, initialResponseContainer, }) {
const marshaller = this.marshaller;
const eventStreamMember = responseSchema.getEventStreamMember();
const unionSchema = responseSchema.getMemberSchema(eventStreamMember);
const memberSchemas = unionSchema.getMemberSchemas();
const initialResponseMarker = Symbol("initialResponseMarker");
const asyncIterable = marshaller.deserialize(response.body, async (event) => {
const unionMember = Object.keys(event).find((key) => {
return key !== "__type";
}) ?? "";
if (unionMember === "initial-response") {
const dataObject = await this.deserializer.read(responseSchema, event[unionMember].body);
delete dataObject[eventStreamMember];
return {
[initialResponseMarker]: true,
...dataObject,
};
}
else if (unionMember in memberSchemas) {
const eventStreamSchema = memberSchemas[unionMember];
return {
[unionMember]: await this.deserializer.read(eventStreamSchema, event[unionMember].body),
};
}
else {
return {
$unknown: event,
};
}
});
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
const firstEvent = await asyncIterator.next();
if (firstEvent.done) {
return asyncIterable;
}
if (firstEvent.value?.[initialResponseMarker]) {
if (!responseSchema) {
throw new Error("@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.");
}
for (const [key, value] of Object.entries(firstEvent.value)) {
initialResponseContainer[key] = value;
}
}
return {
async *[Symbol.asyncIterator]() {
if (!firstEvent?.value?.[initialResponseMarker]) {
yield firstEvent.value;
}
while (true) {
const { done, value } = await asyncIterator.next();
if (done) {
break;
}
yield value;
}
},
};
}
writeEventBody(unionMember, unionSchema, event) {
const serializer = this.serializer;
let eventType = unionMember;
let explicitPayloadMember = null;
let explicitPayloadContentType;
const isKnownSchema = unionSchema.hasMemberSchema(unionMember);
const additionalHeaders = {};
if (!isKnownSchema) {
const [type, value] = event[unionMember];
eventType = type;
serializer.write(schema.SCHEMA.DOCUMENT, value);
}
else {
const eventSchema = unionSchema.getMemberSchema(unionMember);
if (eventSchema.isStructSchema()) {
for (const [memberName, memberSchema] of eventSchema.structIterator()) {
const { eventHeader, eventPayload } = memberSchema.getMergedTraits();
if (eventPayload) {
explicitPayloadMember = memberName;
break;
}
else if (eventHeader) {
const value = event[unionMember][memberName];
let type = "binary";
if (memberSchema.isNumericSchema()) {
if ((-2) ** 31 <= value && value <= 2 ** 31 - 1) {
type = "integer";
}
else {
type = "long";
}
}
else if (memberSchema.isTimestampSchema()) {
type = "timestamp";
}
else if (memberSchema.isStringSchema()) {
type = "string";
}
else if (memberSchema.isBooleanSchema()) {
type = "boolean";
}
if (value != null) {
additionalHeaders[memberName] = {
type,
value,
};
delete event[unionMember][memberName];
}
}
}
if (explicitPayloadMember !== null) {
const payloadSchema = eventSchema.getMemberSchema(explicitPayloadMember);
if (payloadSchema.isBlobSchema()) {
explicitPayloadContentType = "application/octet-stream";
}
else if (payloadSchema.isStringSchema()) {
explicitPayloadContentType = "text/plain";
}
serializer.write(payloadSchema, event[unionMember][explicitPayloadMember]);
}
else {
serializer.write(eventSchema, event[unionMember]);
}
}
else {
throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.");
}
}
const messageSerialization = serializer.flush();
const body = typeof messageSerialization === "string"
? (this.serdeContext?.utf8Decoder ?? utilUtf8.fromUtf8)(messageSerialization)
: messageSerialization;
return {
body,
eventType,
explicitPayloadContentType,
additionalHeaders,
};
}
}
exports.EventStreamSerde = EventStreamSerde;
/***/ })
};
;
Generated Vendored
+224
View File
@@ -0,0 +1,224 @@
"use strict";
exports.id = 579;
exports.ids = [579];
exports.modules = {
/***/ 6579:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
var schema = __webpack_require__(6890);
var utilUtf8 = __webpack_require__(1577);
class EventStreamSerde {
marshaller;
serializer;
deserializer;
serdeContext;
defaultContentType;
constructor({ marshaller, serializer, deserializer, serdeContext, defaultContentType, }) {
this.marshaller = marshaller;
this.serializer = serializer;
this.deserializer = deserializer;
this.serdeContext = serdeContext;
this.defaultContentType = defaultContentType;
}
async serializeEventStream({ eventStream, requestSchema, initialRequest, }) {
const marshaller = this.marshaller;
const eventStreamMember = requestSchema.getEventStreamMember();
const unionSchema = requestSchema.getMemberSchema(eventStreamMember);
unionSchema.getMemberSchemas();
const serializer = this.serializer;
const defaultContentType = this.defaultContentType;
const initialRequestMarker = Symbol("initialRequestMarker");
const eventStreamIterable = {
async *[Symbol.asyncIterator]() {
if (initialRequest) {
const headers = {
":event-type": { type: "string", value: "initial-request" },
":message-type": { type: "string", value: "event" },
":content-type": { type: "string", value: defaultContentType },
};
serializer.write(requestSchema, initialRequest);
const body = serializer.flush();
yield {
[initialRequestMarker]: true,
headers,
body,
};
}
for await (const page of eventStream) {
yield page;
}
},
};
return marshaller.serialize(eventStreamIterable, (event) => {
if (event[initialRequestMarker]) {
return {
headers: event.headers,
body: event.body,
};
}
const unionMember = Object.keys(event).find((key) => {
return key !== "__type";
}) ?? "";
const { additionalHeaders, body, eventType, explicitPayloadContentType } = this.writeEventBody(unionMember, unionSchema, event);
const headers = {
":event-type": { type: "string", value: eventType },
":message-type": { type: "string", value: "event" },
":content-type": { type: "string", value: explicitPayloadContentType ?? defaultContentType },
...additionalHeaders,
};
return {
headers,
body,
};
});
}
async deserializeEventStream({ response, responseSchema, initialResponseContainer, }) {
const marshaller = this.marshaller;
const eventStreamMember = responseSchema.getEventStreamMember();
const unionSchema = responseSchema.getMemberSchema(eventStreamMember);
const memberSchemas = unionSchema.getMemberSchemas();
const initialResponseMarker = Symbol("initialResponseMarker");
const asyncIterable = marshaller.deserialize(response.body, async (event) => {
const unionMember = Object.keys(event).find((key) => {
return key !== "__type";
}) ?? "";
if (unionMember === "initial-response") {
const dataObject = await this.deserializer.read(responseSchema, event[unionMember].body);
delete dataObject[eventStreamMember];
return {
[initialResponseMarker]: true,
...dataObject,
};
}
else if (unionMember in memberSchemas) {
const eventStreamSchema = memberSchemas[unionMember];
return {
[unionMember]: await this.deserializer.read(eventStreamSchema, event[unionMember].body),
};
}
else {
return {
$unknown: event,
};
}
});
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
const firstEvent = await asyncIterator.next();
if (firstEvent.done) {
return asyncIterable;
}
if (firstEvent.value?.[initialResponseMarker]) {
if (!responseSchema) {
throw new Error("@smithy::core/protocols - initial-response event encountered in event stream but no response schema given.");
}
for (const [key, value] of Object.entries(firstEvent.value)) {
initialResponseContainer[key] = value;
}
}
return {
async *[Symbol.asyncIterator]() {
if (!firstEvent?.value?.[initialResponseMarker]) {
yield firstEvent.value;
}
while (true) {
const { done, value } = await asyncIterator.next();
if (done) {
break;
}
yield value;
}
},
};
}
writeEventBody(unionMember, unionSchema, event) {
const serializer = this.serializer;
let eventType = unionMember;
let explicitPayloadMember = null;
let explicitPayloadContentType;
const isKnownSchema = unionSchema.hasMemberSchema(unionMember);
const additionalHeaders = {};
if (!isKnownSchema) {
const [type, value] = event[unionMember];
eventType = type;
serializer.write(schema.SCHEMA.DOCUMENT, value);
}
else {
const eventSchema = unionSchema.getMemberSchema(unionMember);
if (eventSchema.isStructSchema()) {
for (const [memberName, memberSchema] of eventSchema.structIterator()) {
const { eventHeader, eventPayload } = memberSchema.getMergedTraits();
if (eventPayload) {
explicitPayloadMember = memberName;
break;
}
else if (eventHeader) {
const value = event[unionMember][memberName];
let type = "binary";
if (memberSchema.isNumericSchema()) {
if ((-2) ** 31 <= value && value <= 2 ** 31 - 1) {
type = "integer";
}
else {
type = "long";
}
}
else if (memberSchema.isTimestampSchema()) {
type = "timestamp";
}
else if (memberSchema.isStringSchema()) {
type = "string";
}
else if (memberSchema.isBooleanSchema()) {
type = "boolean";
}
if (value != null) {
additionalHeaders[memberName] = {
type,
value,
};
delete event[unionMember][memberName];
}
}
}
if (explicitPayloadMember !== null) {
const payloadSchema = eventSchema.getMemberSchema(explicitPayloadMember);
if (payloadSchema.isBlobSchema()) {
explicitPayloadContentType = "application/octet-stream";
}
else if (payloadSchema.isStringSchema()) {
explicitPayloadContentType = "text/plain";
}
serializer.write(payloadSchema, event[unionMember][explicitPayloadMember]);
}
else {
serializer.write(eventSchema, event[unionMember]);
}
}
else {
throw new Error("@smithy/core/event-streams - non-struct member not supported in event stream union.");
}
}
const messageSerialization = serializer.flush();
const body = typeof messageSerialization === "string"
? (this.serdeContext?.utf8Decoder ?? utilUtf8.fromUtf8)(messageSerialization)
: messageSerialization;
return {
body,
eventType,
explicitPayloadContentType,
additionalHeaders,
};
}
}
exports.EventStreamSerde = EventStreamSerde;
/***/ })
};
;
Generated Vendored
+7819 -8533
View File
File diff suppressed because it is too large Load Diff
Generated Vendored
+40012 -8546
View File
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@ jobs:
sast-guard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: grolston/guard-action@main
with:
data_directory: './cloudformation/' ## change to your template directory
+2 -2
View File
@@ -18,9 +18,9 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@v5
with:
aws-region: us-east-1
## the following creates an ARN based on the values entered into github secrets
-31
View File
@@ -1,31 +0,0 @@
/** @type {import('jest').Config} */
const config = {
verbose: true,
transform: {
'^.+\\.m?[tj]sx?$': ['ts-jest'],
},
testMatch: [
'<rootDir>/src/**/__tests__/**/*.ts?(x)',
'<rootDir>/(test|src)/**/*(*.)@(spec|test).ts?(x)',
'<rootDir>/test/**/*.(test|spec).(js|jsx|ts|tsx)',
],
clearMocks: true,
collectCoverage: true,
coverageReporters: ['json', 'lcov', 'clover', 'cobertura', 'text'],
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: ['/node_modules/'],
testPathIgnorePatterns: ['/node_modules/'],
watchPathIgnorePatterns: ['/node_modules/'],
reporters: [
'default',
[
'jest-junit',
{
outputDirectory: 'test-reports',
},
],
],
preset: 'ts-jest/presets/default-legacy',
};
module.exports = config;
+956 -646
View File
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -1,7 +1,7 @@
{
"name": "configure-aws-credentials",
"description": "A GitHub Action to configure AWS credentials",
"version": "5.0.0",
"version": "5.1.0",
"scripts": {
"build": "tsc",
"lint": "biome check --error-on-warnings ./src",
@@ -16,26 +16,26 @@
"organization": true
},
"devDependencies": {
"@aws-sdk/credential-provider-env": "^3.879.0",
"@biomejs/biome": "2.2.2",
"@aws-sdk/credential-provider-env": "^3.901.0",
"@biomejs/biome": "2.2.4",
"@smithy/property-provider": "^4.0.3",
"@types/node": "^24.3.0",
"@vercel/ncc": "^0.38.3",
"@types/node": "^24.6.0",
"@vercel/ncc": "^0.38.4",
"@vitest/coverage-v8": "^3.2.4",
"aws-sdk-client-mock": "^4.1.0",
"cpy-cli": "^6.0.0",
"del-cli": "^6.0.0",
"del-cli": "^7.0.0",
"json-schema": "^0.4.0",
"memfs": "^4.38.2",
"memfs": "^4.47.0",
"standard-version": "^9.5.0",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
},
"dependencies": {
"@actions/core": "^1.11.1",
"@aws-sdk/client-sts": "^3.873.0",
"@smithy/node-http-handler": "^4.1.1",
"https-proxy-agent": "^7.0.6"
"@aws-sdk/client-sts": "^3.899.0",
"@smithy/node-http-handler": "^4.2.0",
"proxy-agent": "^6.5.0"
},
"keywords": [
"aws",
-1
View File
@@ -10,6 +10,5 @@
"exclude-paths": [".github", "dist"]
}
},
"last-release-sha": "7474bc4690e29a8392af63c5b98e7449536d5c3a",
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
}
+27 -12
View File
@@ -2,14 +2,16 @@ import { info } from '@actions/core';
import { STSClient } from '@aws-sdk/client-sts';
import type { AwsCredentialIdentity } from '@aws-sdk/types';
import { NodeHttpHandler } from '@smithy/node-http-handler';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { ProxyAgent } from 'proxy-agent';
import { errorMessage, getCallerIdentity } from './helpers';
import { ProxyResolver } from './ProxyResolver';
const USER_AGENT = 'configure-aws-credentials-for-github-actions';
export interface CredentialsClientProps {
region?: string;
proxyServer?: string;
noProxy?: string;
}
export class CredentialsClient {
@@ -18,24 +20,37 @@ export class CredentialsClient {
private readonly requestHandler?: NodeHttpHandler;
constructor(props: CredentialsClientProps) {
this.region = props.region;
if (props.region !== undefined) {
this.region = props.region;
}
if (props.proxyServer) {
info('Configuring proxy handler for STS client');
const handler = new HttpsProxyAgent(props.proxyServer);
const proxyOptions: { httpProxy: string; httpsProxy: string; noProxy?: string } = {
httpProxy: props.proxyServer,
httpsProxy: props.proxyServer,
};
if (props.noProxy !== undefined) {
proxyOptions.noProxy = props.noProxy;
}
const getProxyForUrl = new ProxyResolver(proxyOptions).getProxyForUrl;
const handler = new ProxyAgent({ getProxyForUrl });
this.requestHandler = new NodeHttpHandler({
httpAgent: handler,
httpsAgent: handler,
httpAgent: handler,
});
}
}
public get stsClient(): STSClient {
if (!this._stsClient) {
this._stsClient = new STSClient({
region: this.region,
customUserAgent: USER_AGENT,
requestHandler: this.requestHandler ? this.requestHandler : undefined,
});
const config = { customUserAgent: USER_AGENT } as {
customUserAgent: string;
region?: string;
requestHandler?: NodeHttpHandler;
};
if (this.region !== undefined) config.region = this.region;
if (this.requestHandler !== undefined) config.requestHandler = this.requestHandler;
this._stsClient = new STSClient(config);
}
return this._stsClient;
}
@@ -81,9 +96,9 @@ export class CredentialsClient {
}
private async loadCredentials() {
const client = new STSClient({
requestHandler: this.requestHandler ? this.requestHandler : undefined,
});
const config = {} as { requestHandler?: NodeHttpHandler };
if (this.requestHandler !== undefined) config.requestHandler = this.requestHandler;
const client = new STSClient(config);
return client.config.credentials();
}
}
+71
View File
@@ -0,0 +1,71 @@
// Based on https://github.com/Rob--W/proxy-from-env/tree/caf8c32301afdac8b5feaf346028bd8240690144
// See https://github.com/Rob--W/proxy-from-env/blob/caf8c32301afdac8b5feaf346028bd8240690144/LICENSE
import type * as http from 'node:http';
const DEFAULT_PORTS: Record<string, number> = {
http: 80,
https: 443,
};
export interface ProxyOptions {
readonly noProxy?: string;
readonly httpsProxy?: string;
readonly httpProxy?: string;
}
export class ProxyResolver {
options: ProxyOptions;
constructor(options: ProxyOptions) {
this.options = options;
}
// This method matches the interface expected by 'proxy-agent'. It is an arrow function to bind 'this'.
public readonly getProxyForUrl = (url: string, _req: http.ClientRequest): string => {
return this.getProxyForUrlOptions(url, this.options);
};
private getProxyForUrlOptions(url: string | URL, options?: ProxyOptions): string {
let parsedUrl: URL;
try {
parsedUrl = typeof url === 'string' ? new URL(url) : url;
} catch (_) {
return ''; // Don't proxy invalid URLs.
}
const proto = parsedUrl.protocol.split(':', 1)[0];
if (!proto) return ''; // Don't proxy URLs without a protocol.
const hostname = parsedUrl.host;
const port = parseInt(parsedUrl.port || '') || DEFAULT_PORTS[proto] || 0;
if (options?.noProxy && !this.shouldProxy(hostname, port, options.noProxy)) return '';
if (proto === 'http' && options?.httpProxy) return options.httpProxy;
if (proto === 'https' && options?.httpsProxy) return options.httpsProxy;
return ''; // No proxy configured for this protocol or unknown protocol
}
private shouldProxy(hostname: string, port: number, noProxy: string): boolean {
if (!noProxy) return true;
if (noProxy === '*') return false; // Never proxy if wildcard is set.
return noProxy.split(/[,\s]/).every((proxy) => {
if (!proxy) return true; // Skip zero-length hosts.
const parsedProxy = proxy.match(/^(.+):(\d+)$/);
const parsedProxyHostname = parsedProxy ? parsedProxy[1] : proxy;
const parsedProxyPort = parsedProxy?.[2] ? parseInt(parsedProxy[2]) : 0;
if (parsedProxyPort && parsedProxyPort !== port) return true; // Skip if ports don't match.
if (parsedProxyHostname && !/^[.*]/.test(parsedProxyHostname)) {
// No wildcards, so stop proxying if there is an exact match.
return hostname !== parsedProxyHostname;
}
let cleanProxyHostname = parsedProxyHostname;
if (parsedProxyHostname && parsedProxyHostname.charAt(0) === '*') {
// Remove leading wildcard.
cleanProxyHostname = parsedProxyHostname.slice(1);
}
// Stop proxying if the hostname ends with the no_proxy host.
return !cleanProxyHostname || !hostname.endsWith(cleanProxyHostname);
});
}
}
+24 -5
View File
@@ -28,6 +28,7 @@ export function translateEnvVariables() {
'RETRY_MAX_ATTEMPTS',
'SPECIAL_CHARACTERS_WORKAROUND',
'USE_EXISTING_CREDENTIALS',
'NO_PROXY',
];
// Treat HTTPS_PROXY as HTTP_PROXY. Precedence is HTTPS_PROXY > HTTP_PROXY
if (process.env.HTTPS_PROXY) process.env.HTTP_PROXY = process.env.HTTPS_PROXY;
@@ -114,7 +115,14 @@ export async function getCallerIdentity(client: STSClient): Promise<{ Account: s
if (!identity.Account || !identity.Arn) {
throw new Error('Could not get Account ID or ARN from STS. Did you set credentials?');
}
return { Account: identity.Account, Arn: identity.Arn, UserId: identity.UserId };
const result: { Account: string; Arn: string; UserId?: string } = {
Account: identity.Account,
Arn: identity.Arn,
};
if (identity.UserId !== undefined) {
result.UserId = identity.UserId;
}
return result;
}
// Obtains account ID from STS Client and sets it as output
@@ -184,15 +192,26 @@ export async function retryAndBackoff<T>(
return await fn();
} catch (err) {
if (!isRetryable) {
core.debug(`retryAndBackoff: error is not retryable: ${errorMessage(err)}`);
throw err;
}
// It's retryable, so sleep and retry.
await sleep(Math.random() * (2 ** retries * base));
retries += 1;
if (retries >= maxRetries) {
const delay = Math.random() * (2 ** retries * base);
const nextRetry = retries + 1;
core.debug(
`retryAndBackoff: attempt ${nextRetry} of ${maxRetries} failed: ${errorMessage(err)}. ` +
`Retrying after ${Math.floor(delay)}ms.`,
);
await sleep(delay);
if (nextRetry >= maxRetries) {
core.debug('retryAndBackoff: reached max retries; giving up.');
throw err;
}
return await retryAndBackoff(fn, isRetryable, maxRetries, retries, base);
return await retryAndBackoff(fn, isRetryable, maxRetries, nextRetry, base);
}
}
+19 -2
View File
@@ -56,6 +56,17 @@ export async function run() {
.split(',')
.map((s) => s.trim());
const forceSkipOidc = getBooleanInput('force-skip-oidc', { required: false });
const noProxy = core.getInput('no-proxy', { required: false });
const globalTimeout = Number.parseInt(core.getInput('action-timeout-s', { required: false })) || 0;
let timeoutId: NodeJS.Timeout | undefined;
if (globalTimeout > 0) {
core.info(`Setting a global timeout of ${globalTimeout} seconds for the action`);
timeoutId = setTimeout(() => {
core.setFailed(`Action timed out after ${globalTimeout} seconds`);
process.exit(1);
}, globalTimeout * 1000);
}
if (forceSkipOidc && roleToAssume && !AccessKeyId && !webIdentityTokenFile) {
throw new Error(
@@ -109,7 +120,10 @@ export async function run() {
exportRegion(region, outputEnvCredentials);
// Instantiate credentials client
const credentialsClient = new CredentialsClient({ region, proxyServer });
const clientProps: { region: string; proxyServer?: string; noProxy?: string } = { region };
if (proxyServer) clientProps.proxyServer = proxyServer;
if (noProxy) clientProps.noProxy = noProxy;
const credentialsClient = new CredentialsClient(clientProps);
let sourceAccountId: string;
let webIdentityToken: string;
@@ -118,6 +132,7 @@ export async function run() {
const validCredentials = await areCredentialsValid(credentialsClient);
if (validCredentials) {
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
if (timeoutId) clearTimeout(timeoutId);
return;
}
core.notice('No valid credentials exist. Running as normal.');
@@ -203,11 +218,13 @@ export async function run() {
} else {
core.info('Proceeding with IAM user credentials');
}
// Clear timeout on successful completion
if (timeoutId) clearTimeout(timeoutId);
} catch (error) {
core.setFailed(errorMessage(error));
const showStackTrace = process.env.SHOW_STACK_TRACE;
if (showStackTrace === 'true') {
throw error;
}
+100
View File
@@ -0,0 +1,100 @@
import type * as http from 'node:http';
import { describe, expect, test } from 'vitest';
import { type ProxyOptions, ProxyResolver } from '../src/ProxyResolver';
describe('ProxyResolver', () => {
const mockReq = {} as http.ClientRequest;
test('returns http proxy for http URLs', () => {
const options: ProxyOptions = { httpProxy: 'http://proxy:8080' };
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('http://proxy:8080');
});
test('returns https proxy for https URLs', () => {
const options: ProxyOptions = { httpsProxy: 'https://proxy:8080' };
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('https://example.com', mockReq)).toBe('https://proxy:8080');
});
test('returns empty string when no proxy configured', () => {
const resolver = new ProxyResolver({});
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('');
});
test('respects noProxy setting', () => {
const options: ProxyOptions = {
httpProxy: 'http://proxy:8080',
noProxy: 'example.com',
};
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('');
expect(resolver.getProxyForUrl('http://other.com', mockReq)).toBe('http://proxy:8080');
});
test('handles invalid URLs', () => {
const resolver = new ProxyResolver({ httpProxy: 'http://proxy:8080' });
expect(resolver.getProxyForUrl('invalid-url', mockReq)).toBe('');
});
test('handles wildcard noProxy', () => {
const options: ProxyOptions = {
httpProxy: 'http://proxy:8080',
noProxy: '*',
};
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('');
});
test('handles comma-separated noProxy list', () => {
const options: ProxyOptions = {
httpProxy: 'http://proxy:8080',
noProxy: 'example.com,test.com',
};
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('');
expect(resolver.getProxyForUrl('http://test.com', mockReq)).toBe('');
expect(resolver.getProxyForUrl('http://other.com', mockReq)).toBe('http://proxy:8080');
});
test('handles port-specific noProxy', () => {
const options: ProxyOptions = {
httpProxy: 'http://proxy:8080',
noProxy: 'example.com:80',
};
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('');
expect(resolver.getProxyForUrl('http://example.com:8080', mockReq)).toBe('http://proxy:8080');
});
test('handles wildcard domain noProxy', () => {
const options: ProxyOptions = {
httpProxy: 'http://proxy:8080',
noProxy: '*.example.com',
};
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('http://sub.example.com', mockReq)).toBe('');
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('http://proxy:8080');
expect(resolver.getProxyForUrl('http://other.com', mockReq)).toBe('http://proxy:8080');
});
test('handles empty noProxy entries', () => {
const options: ProxyOptions = {
httpProxy: 'http://proxy:8080',
noProxy: 'example.com, ,test.com',
};
const resolver = new ProxyResolver(options);
expect(resolver.getProxyForUrl('http://example.com', mockReq)).toBe('');
expect(resolver.getProxyForUrl('http://test.com', mockReq)).toBe('');
});
});
+35
View File
@@ -61,4 +61,39 @@ describe('Configure AWS Credentials helpers', {}, () => {
expect(core.setSecret).toHaveBeenCalledTimes(3);
expect(core.exportVariable).toHaveBeenCalledTimes(0);
});
it('verifies credentials without special characters', {}, () => {
expect(helpers.verifyKeys({ AccessKeyId: 'AKIATEST', SecretAccessKey: 'secretkey' })).toBe(true);
expect(helpers.verifyKeys({ AccessKeyId: 'AKIA!@#$', SecretAccessKey: 'secret' })).toBe(false);
expect(helpers.verifyKeys(undefined)).toBe(false);
});
it('translates environment variables', {}, () => {
process.env.AWS_REGION = 'us-east-1';
process.env.HTTPS_PROXY = 'https://proxy:8080';
helpers.translateEnvVariables();
expect(process.env['INPUT_AWS-REGION']).toBe('us-east-1');
expect(process.env.HTTP_PROXY).toBe('https://proxy:8080');
});
it('handles getBooleanInput correctly', {}, () => {
vi.spyOn(core, 'getInput').mockReturnValue('true');
expect(helpers.getBooleanInput('test')).toBe(true);
vi.spyOn(core, 'getInput').mockReturnValue('false');
expect(helpers.getBooleanInput('test')).toBe(false);
vi.spyOn(core, 'getInput').mockReturnValue('');
expect(helpers.getBooleanInput('test', { default: true })).toBe(true);
vi.spyOn(core, 'getInput').mockReturnValue('invalid');
expect(() => helpers.getBooleanInput('test')).toThrow();
});
it('clears session token when not provided', {}, () => {
vi.spyOn(core, 'exportVariable').mockImplementation(() => {});
process.env.AWS_SESSION_TOKEN = 'old-token';
helpers.exportCredentials({ AccessKeyId: 'test', SecretAccessKey: 'test' }, false, true);
expect(core.exportVariable).toHaveBeenCalledWith('AWS_SESSION_TOKEN', '');
});
});
+240 -110
View File
@@ -341,10 +341,12 @@ describe('Configure AWS Credentials', {}, () => {
});
it('skips OIDC when force-skip-oidc is true with IAM credentials', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_ASSUMEROLE_INPUTS,
'force-skip-oidc': 'true'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_ASSUMEROLE_INPUTS,
'force-skip-oidc': 'true',
}),
);
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
@@ -353,17 +355,19 @@ describe('Configure AWS Credentials', {}, () => {
.mockResolvedValueOnce({ accessKeyId: 'MYAWSACCESSKEYID' })
.mockResolvedValueOnce({ accessKeyId: 'STSAWSACCESSKEYID' });
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
await run();
expect(core.getIDToken).not.toHaveBeenCalled();
expect(core.setFailed).not.toHaveBeenCalled();
});
it('skips OIDC when force-skip-oidc is true with web identity token file', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.WEBIDENTITY_TOKEN_FILE_INPUTS,
'force-skip-oidc': 'true'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.WEBIDENTITY_TOKEN_FILE_INPUTS,
'force-skip-oidc': 'true',
}),
);
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
@@ -372,7 +376,7 @@ describe('Configure AWS Credentials', {}, () => {
vol.reset();
fs.mkdirSync('/home/github', { recursive: true });
fs.writeFileSync('/home/github/file.txt', 'test-token');
await run();
expect(core.getIDToken).not.toHaveBeenCalled();
expect(core.info).toHaveBeenCalledWith('Assuming role with web identity token file');
@@ -380,34 +384,38 @@ describe('Configure AWS Credentials', {}, () => {
});
it('fails when force-skip-oidc is true but no alternative credentials provided', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
'aws-region': 'fake-region-1',
'force-skip-oidc': 'true'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
'aws-region': 'fake-region-1',
'force-skip-oidc': 'true',
}),
);
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
await run();
expect(core.setFailed).toHaveBeenCalledWith(
"If 'force-skip-oidc' is true and 'role-to-assume' is set, 'aws-access-key-id' or 'web-identity-token-file' must be set"
"If 'force-skip-oidc' is true and 'role-to-assume' is set, 'aws-access-key-id' or 'web-identity-token-file' must be set",
);
});
it('allows force-skip-oidc without role-to-assume', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'force-skip-oidc': 'true'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'force-skip-oidc': 'true',
}),
);
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
await run();
expect(core.getIDToken).not.toHaveBeenCalled();
expect(core.info).toHaveBeenCalledWith('Proceeding with IAM user credentials');
@@ -415,15 +423,17 @@ describe('Configure AWS Credentials', {}, () => {
});
it('uses OIDC when force-skip-oidc is false (default behavior)', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'force-skip-oidc': 'false'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'force-skip-oidc': 'false',
}),
);
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
await run();
expect(core.getIDToken).toHaveBeenCalledWith('');
expect(core.info).toHaveBeenCalledWith('Assuming role with OIDC');
@@ -436,7 +446,7 @@ describe('Configure AWS Credentials', {}, () => {
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
await run();
expect(core.getIDToken).toHaveBeenCalledWith('');
expect(core.info).toHaveBeenCalledWith('Assuming role with OIDC');
@@ -444,12 +454,14 @@ describe('Configure AWS Credentials', {}, () => {
});
it('works with role chaining when force-skip-oidc is true', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.EXISTING_ROLE_INPUTS,
'force-skip-oidc': 'true',
'aws-access-key-id': 'MYAWSACCESSKEYID',
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.EXISTING_ROLE_INPUTS,
'force-skip-oidc': 'true',
'aws-access-key-id': 'MYAWSACCESSKEYID',
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
}),
);
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
@@ -458,164 +470,262 @@ describe('Configure AWS Credentials', {}, () => {
.mockResolvedValueOnce({ accessKeyId: 'MYAWSACCESSKEYID' })
.mockResolvedValueOnce({ accessKeyId: 'STSAWSACCESSKEYID' });
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
await run();
expect(core.getIDToken).not.toHaveBeenCalled();
expect(core.setFailed).not.toHaveBeenCalled();
});
});
describe('Account ID Validation', {}, () => {
describe('Account ID Validation', {}, () => {
beforeEach(() => {
vi.clearAllMocks();
mockedSTSClient.reset();
});
it('succeeds when account ID matches allowed list', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '111111111111'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '111111111111',
}),
);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
await run();
expect(core.setFailed).not.toHaveBeenCalled();
expect(core.info).toHaveBeenCalledWith('Proceeding with IAM user credentials');
});
it('succeeds with multiple allowed account IDs when account matches', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '999999999999,111111111111,222222222222'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '999999999999,111111111111,222222222222',
}),
);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
await run();
expect(core.setFailed).not.toHaveBeenCalled();
});
it('fails when account ID does not match allowed list', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '999999999999'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '999999999999',
}),
);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
await run();
expect(core.setFailed).toHaveBeenCalledWith(
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999'
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999',
);
});
it('fails when account ID does not match any in multiple allowed accounts', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '999999999999,888888888888'
}));
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '999999999999,888888888888',
}),
);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
});
await run();
expect(core.setFailed).toHaveBeenCalledWith(
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999, 888888888888'
'The account ID of the provided credentials (111111111111) does not match any of the expected account IDs: 999999999999, 888888888888',
);
});
it('works with assume role when account ID matches', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_ASSUMEROLE_INPUTS,
'allowed-account-ids': '111111111111'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_ASSUMEROLE_INPUTS,
'allowed-account-ids': '111111111111',
}),
);
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials')
.mockResolvedValueOnce({ accessKeyId: 'MYAWSACCESSKEYID' })
.mockResolvedValueOnce({ accessKeyId: 'STSAWSACCESSKEYID' });
await run();
expect(core.setFailed).not.toHaveBeenCalled();
expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID');
});
it('works with OIDC when account ID matches', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'allowed-account-ids': '111111111111'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'allowed-account-ids': '111111111111',
}),
);
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
await run();
expect(core.setFailed).not.toHaveBeenCalled();
await run();
expect(core.setFailed).not.toHaveBeenCalled();
expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID');
});
it('handles GetCallerIdentity API failure gracefully', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '111111111111'
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '111111111111',
}),
);
mockedSTSClient.on(GetCallerIdentityCommand).rejects(new Error('API Error'));
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
await run();
expect(core.setFailed).toHaveBeenCalledWith('Could not validate account ID of credentials: API Error');
});
it('ignores validation when allowed-account-ids is empty', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': ''
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': '',
}),
);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
await run();
expect(core.setFailed).not.toHaveBeenCalled();
expect(core.info).toHaveBeenCalledWith('Proceeding with IAM user credentials');
});
it('handles whitespace in allowed-account-ids input', async () => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': ' 111111111111 , 222222222222 '
}));
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'allowed-account-ids': ' 111111111111 , 222222222222 ',
}),
);
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
accessKeyId: 'MYAWSACCESSKEYID',
});
await run();
expect(core.setFailed).not.toHaveBeenCalled();
});
});
});
describe('Global Timeout Configuration', {}, () => {
beforeEach(() => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS));
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValueOnce({
accessKeyId: 'MYAWSACCESSKEYID',
});
});
it('sets timeout when action-timeout-s is provided', async () => {
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');
const infoSpy = vi.spyOn(core, 'info');
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'action-timeout-s': '30',
}),
);
await run();
expect(infoSpy).toHaveBeenCalledWith('Setting a global timeout of 30 seconds for the action');
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 30000);
expect(clearTimeoutSpy).toHaveBeenCalledWith(expect.any(Object));
expect(core.setFailed).not.toHaveBeenCalled();
});
it('does not set timeout when action-timeout-s is 0', async () => {
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
const infoSpy = vi.spyOn(core, 'info');
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'action-timeout-s': '0',
}),
);
await run();
expect(infoSpy).not.toHaveBeenCalledWith(expect.stringContaining('Setting a global timeout'));
expect(setTimeoutSpy).not.toHaveBeenCalled();
expect(core.setFailed).not.toHaveBeenCalled();
});
it('does not set timeout when action-timeout-s is not provided', async () => {
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
const infoSpy = vi.spyOn(core, 'info');
await run();
expect(infoSpy).not.toHaveBeenCalledWith(expect.stringContaining('Setting a global timeout'));
expect(setTimeoutSpy).not.toHaveBeenCalled();
expect(core.setFailed).not.toHaveBeenCalled();
});
it('timeout callback calls setFailed and exits process', async () => {
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
const processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.IAM_USER_INPUTS,
'action-timeout-s': '5',
}),
);
await run();
// Get the timeout callback function
const timeoutCallback = setTimeoutSpy.mock.calls[0][0] as () => void;
// Execute the timeout callback
timeoutCallback();
expect(core.setFailed).toHaveBeenCalledWith('Action timed out after 5 seconds');
expect(processExitSpy).toHaveBeenCalledWith(1);
});
});
describe('HTTP Proxy Configuration', {}, () => {
beforeEach(() => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
@@ -630,12 +740,12 @@ describe('Configure AWS Credentials', {}, () => {
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'http-proxy': 'http://proxy.example.com:8080'
})
'http-proxy': 'http://proxy.example.com:8080',
}),
);
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
@@ -643,9 +753,9 @@ describe('Configure AWS Credentials', {}, () => {
it('configures proxy from HTTP_PROXY environment variable', async () => {
const infoSpy = vi.spyOn(core, 'info');
process.env.HTTP_PROXY = 'http://proxy.example.com:8080';
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
@@ -653,9 +763,9 @@ describe('Configure AWS Credentials', {}, () => {
it('configures proxy from HTTPS_PROXY environment variable', async () => {
const infoSpy = vi.spyOn(core, 'info');
process.env.HTTPS_PROXY = 'https://proxy.example.com:8080';
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
@@ -666,30 +776,50 @@ describe('Configure AWS Credentials', {}, () => {
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'http-proxy': 'http://input-proxy.example.com:8080'
})
'http-proxy': 'http://input-proxy.example.com:8080',
}),
);
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
it('properly configures proxy agent in STS client', async () => {
const infoSpy = vi.spyOn(core, 'info');
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'http-proxy': 'http://proxy.example.com:8080'
})
'http-proxy': 'http://proxy.example.com:8080',
}),
);
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
it('configures no-proxy setting', async () => {
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'http-proxy': 'http://proxy.example.com:8080',
'no-proxy': 'localhost,127.0.0.1',
}),
);
await run();
expect(core.setFailed).not.toHaveBeenCalled();
});
it('works without proxy configuration', async () => {
await run();
expect(core.setFailed).not.toHaveBeenCalled();
});
});
});
+1 -1
View File
@@ -3,7 +3,7 @@
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"strict": true,
"exactOptionalPropertyTypes": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,