mirror of
https://github.com/aws-actions/configure-aws-credentials.git
synced 2026-09-02 05:55:10 +09:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00943011d9 | |||
| e381dc357a | |||
| 031a65bdc1 | |||
| 9efe715a4b | |||
| 6bbafa679f | |||
| 4c879b4d5d | |||
| fb52c697f2 | |||
| f226b0540e | |||
| be2cf0c768 | |||
| 0be22f94c3 | |||
| 555a8e9ce6 | |||
| 6e562ac9ab | |||
| 4283504248 | |||
| 3146acf819 | |||
| 8688f2d5c1 | |||
| 56c08aa702 | |||
| eda31a964a | |||
| 8b7cc57256 | |||
| 351d894493 | |||
| 1584b8b0e2 | |||
| b845e0f4f4 | |||
| cea42985ac | |||
| 6b19f7ffe6 | |||
| 97ef425d73 | |||
| 6bf15574d0 | |||
| 3cb849cb01 | |||
| dde9b22a8e | |||
| 5ebd15afc6 | |||
| c7a2280e36 | |||
| 3de82418a6 | |||
| a3143e85cf | |||
| 3c23363139 | |||
| 44e57d426c | |||
| b876879204 | |||
| 8e2d02296b | |||
| fb3078e4c8 | |||
| 0994a359d5 | |||
| 1ec058b7a1 | |||
| 163b496f8b | |||
| f2e9d495b0 |
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Configure AWS credentials
|
- name: Configure AWS credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
uses: aws-actions/configure-aws-credentials@v5
|
||||||
with:
|
with:
|
||||||
aws-region: us-west-2
|
aws-region: us-west-2
|
||||||
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
|
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ jobs:
|
|||||||
- name: Get Metadata
|
- name: Get Metadata
|
||||||
id: dependabot-metadata
|
id: dependabot-metadata
|
||||||
uses: dependabot/fetch-metadata@v2
|
uses: dependabot/fetch-metadata@v2
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
name: Clone repo
|
name: Clone repo
|
||||||
- name: Configure AWS credentials
|
- name: Configure AWS credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
uses: aws-actions/configure-aws-credentials@v5
|
||||||
with:
|
with:
|
||||||
aws-region: us-west-2
|
aws-region: us-west-2
|
||||||
role-to-assume: ${{ secrets.CONFIGUREAWSCREDENTIALSPACKAGEROLEARN }}
|
role-to-assume: ${{ secrets.CONFIGUREAWSCREDENTIALSPACKAGEROLEARN }}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ github.ref_name }}
|
ref: ${{ github.ref_name }}
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
npm test
|
npm test
|
||||||
npm run package
|
npm run package
|
||||||
- name: Configure AWS credentials
|
- name: Configure AWS credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
uses: aws-actions/configure-aws-credentials@v5
|
||||||
with:
|
with:
|
||||||
aws-region: us-west-2
|
aws-region: us-west-2
|
||||||
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
|
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
uses: aws-actions/configure-aws-credentials@v5
|
||||||
with:
|
with:
|
||||||
aws-region: us-west-2
|
aws-region: us-west-2
|
||||||
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
|
role-to-assume: ${{ secrets.SECRETS_AWS_PACKAGING_ROLE_TO_ASSUME }}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout repostiory"
|
- name: "Checkout repostiory"
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
- name: "Setup node"
|
- name: "Setup node"
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
".release-please-manifest.json": "4.0.2",
|
".release-please-manifest.json": "4.0.2",
|
||||||
"package.json": "4.0.2",
|
"package.json": "4.0.2",
|
||||||
".": "5.0.0"
|
".": "5.1.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
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)
|
## [5.0.0](https://github.com/aws-actions/configure-aws-credentials/compare/v4.3.1...v5.0.0) (2025-09-03)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
|
| 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 |
|
| 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 |
|
| 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>
|
</details>
|
||||||
|
|
||||||
#### Adjust the retry mechanism
|
#### Adjust the retry mechanism
|
||||||
@@ -180,7 +181,7 @@ this action will always consider the `HTTP_PROXY` environment variable.
|
|||||||
|
|
||||||
Manually configured proxy:
|
Manually configured proxy:
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: my-github-actions-role
|
role-to-assume: my-github-actions-role
|
||||||
@@ -251,13 +252,13 @@ line.
|
|||||||
<summary>Inline session policy examples</summary>
|
<summary>Inline session policy examples</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
inline-session-policy: '{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:List*","Resource":"*"}]}'
|
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:
|
Or we can have a nicely formatted JSON as well:
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
inline-session-policy: >-
|
inline-session-policy: >-
|
||||||
{
|
{
|
||||||
@@ -283,13 +284,13 @@ the role.
|
|||||||
<summary>Managed session policy examples</summary>
|
<summary>Managed session policy examples</summary>
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
managed-session-policies: arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
managed-session-policies: arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
||||||
```
|
```
|
||||||
And we can pass multiple managed policies likes this:
|
And we can pass multiple managed policies likes this:
|
||||||
```yaml
|
```yaml
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
managed-session-policies: |
|
managed-session-policies: |
|
||||||
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
|
||||||
@@ -327,7 +328,7 @@ You can specify the audience through the `audience` input:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials for China region audience
|
- 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:
|
with:
|
||||||
audience: sts.amazonaws.com.cn
|
audience: sts.amazonaws.com.cn
|
||||||
aws-region: cn-northwest-1
|
aws-region: cn-northwest-1
|
||||||
@@ -401,7 +402,7 @@ Examples
|
|||||||
### AssumeRoleWithWebIdentity
|
### AssumeRoleWithWebIdentity
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
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
|
### AssumeRole with role previously assumed by action in same workflow
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
||||||
role-session-name: MySessionName
|
role-session-name: MySessionName
|
||||||
- name: Configure other AWS Credentials
|
- name: Configure other AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::987654321000:role/my-second-role
|
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
|
### AssumeRole with static IAM credentials in repository secrets
|
||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
@@ -455,7 +456,7 @@ name, like `role-to-assume: my-github-actions-role`.
|
|||||||
```yaml
|
```yaml
|
||||||
- name: Configure AWS Credentials 1
|
- name: Configure AWS Credentials 1
|
||||||
id: creds
|
id: creds
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
role-to-assume: arn:aws:iam::123456789100:role/my-github-actions-role
|
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: |
|
run: |
|
||||||
aws sts get-caller-identity
|
aws sts get-caller-identity
|
||||||
- name: Configure AWS Credentials 2
|
- name: Configure AWS Credentials 2
|
||||||
uses: aws-actions/configure-aws-credentials@v4.3.1
|
uses: aws-actions/configure-aws-credentials@v5.0.0
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-2
|
aws-region: us-east-2
|
||||||
aws-access-key-id: ${{ steps.creds.outputs.aws-access-key-id }}
|
aws-access-key-id: ${{ steps.creds.outputs.aws-access-key-id }}
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ inputs:
|
|||||||
http-proxy:
|
http-proxy:
|
||||||
description: Proxy to use for the AWS SDK agent
|
description: Proxy to use for the AWS SDK agent
|
||||||
required: false
|
required: false
|
||||||
|
no-proxy:
|
||||||
|
description: Hosts to skip for the proxy configuration
|
||||||
|
required: false
|
||||||
mask-aws-account-id:
|
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
|
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
|
required: false
|
||||||
@@ -86,6 +89,9 @@ inputs:
|
|||||||
force-skip-oidc:
|
force-skip-oidc:
|
||||||
required: false
|
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.
|
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:
|
outputs:
|
||||||
aws-account-id:
|
aws-account-id:
|
||||||
|
|||||||
+224
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
|
||||||
|
};
|
||||||
|
;
|
||||||
+224
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
|
||||||
|
};
|
||||||
|
;
|
||||||
+4298
-5012
File diff suppressed because it is too large
Load Diff
+36490
-5024
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@ jobs:
|
|||||||
sast-guard:
|
sast-guard:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: grolston/guard-action@main
|
- uses: grolston/guard-action@main
|
||||||
with:
|
with:
|
||||||
data_directory: './cloudformation/' ## change to your template directory
|
data_directory: './cloudformation/' ## change to your template directory
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
- name: Configure AWS Credentials
|
- name: Configure AWS Credentials
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
uses: aws-actions/configure-aws-credentials@v5
|
||||||
with:
|
with:
|
||||||
aws-region: us-east-1
|
aws-region: us-east-1
|
||||||
## the following creates an ARN based on the values entered into github secrets
|
## the following creates an ARN based on the values entered into github secrets
|
||||||
|
|||||||
@@ -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;
|
|
||||||
Generated
+956
-646
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "configure-aws-credentials",
|
"name": "configure-aws-credentials",
|
||||||
"description": "A GitHub Action to configure AWS credentials",
|
"description": "A GitHub Action to configure AWS credentials",
|
||||||
"version": "5.0.0",
|
"version": "5.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"lint": "biome check --error-on-warnings ./src",
|
"lint": "biome check --error-on-warnings ./src",
|
||||||
@@ -16,26 +16,26 @@
|
|||||||
"organization": true
|
"organization": true
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aws-sdk/credential-provider-env": "^3.879.0",
|
"@aws-sdk/credential-provider-env": "^3.901.0",
|
||||||
"@biomejs/biome": "2.2.2",
|
"@biomejs/biome": "2.2.4",
|
||||||
"@smithy/property-provider": "^4.0.3",
|
"@smithy/property-provider": "^4.0.3",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.6.0",
|
||||||
"@vercel/ncc": "^0.38.3",
|
"@vercel/ncc": "^0.38.4",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"aws-sdk-client-mock": "^4.1.0",
|
"aws-sdk-client-mock": "^4.1.0",
|
||||||
"cpy-cli": "^6.0.0",
|
"cpy-cli": "^6.0.0",
|
||||||
"del-cli": "^6.0.0",
|
"del-cli": "^7.0.0",
|
||||||
"json-schema": "^0.4.0",
|
"json-schema": "^0.4.0",
|
||||||
"memfs": "^4.38.2",
|
"memfs": "^4.47.0",
|
||||||
"standard-version": "^9.5.0",
|
"standard-version": "^9.5.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^1.11.1",
|
||||||
"@aws-sdk/client-sts": "^3.873.0",
|
"@aws-sdk/client-sts": "^3.899.0",
|
||||||
"@smithy/node-http-handler": "^4.1.1",
|
"@smithy/node-http-handler": "^4.2.0",
|
||||||
"https-proxy-agent": "^7.0.6"
|
"proxy-agent": "^6.5.0"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"aws",
|
"aws",
|
||||||
|
|||||||
@@ -10,6 +10,5 @@
|
|||||||
"exclude-paths": [".github", "dist"]
|
"exclude-paths": [".github", "dist"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"last-release-sha": "7474bc4690e29a8392af63c5b98e7449536d5c3a",
|
|
||||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-11
@@ -2,14 +2,16 @@ import { info } from '@actions/core';
|
|||||||
import { STSClient } from '@aws-sdk/client-sts';
|
import { STSClient } from '@aws-sdk/client-sts';
|
||||||
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
||||||
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
||||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
import { ProxyAgent } from 'proxy-agent';
|
||||||
import { errorMessage, getCallerIdentity } from './helpers';
|
import { errorMessage, getCallerIdentity } from './helpers';
|
||||||
|
import { ProxyResolver } from './ProxyResolver';
|
||||||
|
|
||||||
const USER_AGENT = 'configure-aws-credentials-for-github-actions';
|
const USER_AGENT = 'configure-aws-credentials-for-github-actions';
|
||||||
|
|
||||||
export interface CredentialsClientProps {
|
export interface CredentialsClientProps {
|
||||||
region?: string;
|
region?: string;
|
||||||
proxyServer?: string;
|
proxyServer?: string;
|
||||||
|
noProxy?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CredentialsClient {
|
export class CredentialsClient {
|
||||||
@@ -18,24 +20,37 @@ export class CredentialsClient {
|
|||||||
private readonly requestHandler?: NodeHttpHandler;
|
private readonly requestHandler?: NodeHttpHandler;
|
||||||
|
|
||||||
constructor(props: CredentialsClientProps) {
|
constructor(props: CredentialsClientProps) {
|
||||||
|
if (props.region !== undefined) {
|
||||||
this.region = props.region;
|
this.region = props.region;
|
||||||
|
}
|
||||||
if (props.proxyServer) {
|
if (props.proxyServer) {
|
||||||
info('Configuring proxy handler for STS client');
|
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({
|
this.requestHandler = new NodeHttpHandler({
|
||||||
httpAgent: handler,
|
|
||||||
httpsAgent: handler,
|
httpsAgent: handler,
|
||||||
|
httpAgent: handler,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get stsClient(): STSClient {
|
public get stsClient(): STSClient {
|
||||||
if (!this._stsClient) {
|
if (!this._stsClient) {
|
||||||
this._stsClient = new STSClient({
|
const config = { customUserAgent: USER_AGENT } as {
|
||||||
region: this.region,
|
customUserAgent: string;
|
||||||
customUserAgent: USER_AGENT,
|
region?: string;
|
||||||
requestHandler: this.requestHandler ? this.requestHandler : undefined,
|
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;
|
return this._stsClient;
|
||||||
}
|
}
|
||||||
@@ -81,9 +96,9 @@ export class CredentialsClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async loadCredentials() {
|
private async loadCredentials() {
|
||||||
const client = new STSClient({
|
const config = {} as { requestHandler?: NodeHttpHandler };
|
||||||
requestHandler: this.requestHandler ? this.requestHandler : undefined,
|
if (this.requestHandler !== undefined) config.requestHandler = this.requestHandler;
|
||||||
});
|
const client = new STSClient(config);
|
||||||
return client.config.credentials();
|
return client.config.credentials();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -28,6 +28,7 @@ export function translateEnvVariables() {
|
|||||||
'RETRY_MAX_ATTEMPTS',
|
'RETRY_MAX_ATTEMPTS',
|
||||||
'SPECIAL_CHARACTERS_WORKAROUND',
|
'SPECIAL_CHARACTERS_WORKAROUND',
|
||||||
'USE_EXISTING_CREDENTIALS',
|
'USE_EXISTING_CREDENTIALS',
|
||||||
|
'NO_PROXY',
|
||||||
];
|
];
|
||||||
// Treat HTTPS_PROXY as HTTP_PROXY. Precedence is HTTPS_PROXY > HTTP_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;
|
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) {
|
if (!identity.Account || !identity.Arn) {
|
||||||
throw new Error('Could not get Account ID or ARN from STS. Did you set credentials?');
|
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
|
// Obtains account ID from STS Client and sets it as output
|
||||||
@@ -184,15 +192,26 @@ export async function retryAndBackoff<T>(
|
|||||||
return await fn();
|
return await fn();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!isRetryable) {
|
if (!isRetryable) {
|
||||||
|
core.debug(`retryAndBackoff: error is not retryable: ${errorMessage(err)}`);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// It's retryable, so sleep and retry.
|
// It's retryable, so sleep and retry.
|
||||||
await sleep(Math.random() * (2 ** retries * base));
|
const delay = Math.random() * (2 ** retries * base);
|
||||||
retries += 1;
|
const nextRetry = retries + 1;
|
||||||
if (retries >= maxRetries) {
|
|
||||||
|
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;
|
throw err;
|
||||||
}
|
}
|
||||||
return await retryAndBackoff(fn, isRetryable, maxRetries, retries, base);
|
|
||||||
|
return await retryAndBackoff(fn, isRetryable, maxRetries, nextRetry, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-2
@@ -56,6 +56,17 @@ export async function run() {
|
|||||||
.split(',')
|
.split(',')
|
||||||
.map((s) => s.trim());
|
.map((s) => s.trim());
|
||||||
const forceSkipOidc = getBooleanInput('force-skip-oidc', { required: false });
|
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) {
|
if (forceSkipOidc && roleToAssume && !AccessKeyId && !webIdentityTokenFile) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -109,7 +120,10 @@ export async function run() {
|
|||||||
exportRegion(region, outputEnvCredentials);
|
exportRegion(region, outputEnvCredentials);
|
||||||
|
|
||||||
// Instantiate credentials client
|
// 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 sourceAccountId: string;
|
||||||
let webIdentityToken: string;
|
let webIdentityToken: string;
|
||||||
|
|
||||||
@@ -118,6 +132,7 @@ export async function run() {
|
|||||||
const validCredentials = await areCredentialsValid(credentialsClient);
|
const validCredentials = await areCredentialsValid(credentialsClient);
|
||||||
if (validCredentials) {
|
if (validCredentials) {
|
||||||
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
|
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
core.notice('No valid credentials exist. Running as normal.');
|
core.notice('No valid credentials exist. Running as normal.');
|
||||||
@@ -203,11 +218,13 @@ export async function run() {
|
|||||||
} else {
|
} else {
|
||||||
core.info('Proceeding with IAM user credentials');
|
core.info('Proceeding with IAM user credentials');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear timeout on successful completion
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(errorMessage(error));
|
core.setFailed(errorMessage(error));
|
||||||
|
|
||||||
const showStackTrace = process.env.SHOW_STACK_TRACE;
|
const showStackTrace = process.env.SHOW_STACK_TRACE;
|
||||||
|
|
||||||
if (showStackTrace === 'true') {
|
if (showStackTrace === 'true') {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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('');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -61,4 +61,39 @@ describe('Configure AWS Credentials helpers', {}, () => {
|
|||||||
expect(core.setSecret).toHaveBeenCalledTimes(3);
|
expect(core.setSecret).toHaveBeenCalledTimes(3);
|
||||||
expect(core.exportVariable).toHaveBeenCalledTimes(0);
|
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', '');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+184
-54
@@ -341,10 +341,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('skips OIDC when force-skip-oidc is true with IAM credentials', async () => {
|
it('skips OIDC when force-skip-oidc is true with IAM credentials', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||||
'force-skip-oidc': 'true'
|
'force-skip-oidc': 'true',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||||
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
@@ -360,10 +362,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('skips OIDC when force-skip-oidc is true with web identity token file', async () => {
|
it('skips OIDC when force-skip-oidc is true with web identity token file', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.WEBIDENTITY_TOKEN_FILE_INPUTS,
|
...mocks.WEBIDENTITY_TOKEN_FILE_INPUTS,
|
||||||
'force-skip-oidc': 'true'
|
'force-skip-oidc': 'true',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
@@ -380,24 +384,28 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails when force-skip-oidc is true but no alternative credentials provided', async () => {
|
it('fails when force-skip-oidc is true but no alternative credentials provided', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
|
||||||
'aws-region': 'fake-region-1',
|
'aws-region': 'fake-region-1',
|
||||||
'force-skip-oidc': 'true'
|
'force-skip-oidc': 'true',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
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 () => {
|
it('allows force-skip-oidc without role-to-assume', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'force-skip-oidc': 'true'
|
'force-skip-oidc': 'true',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||||
|
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
@@ -415,10 +423,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uses OIDC when force-skip-oidc is false (default behavior)', async () => {
|
it('uses OIDC when force-skip-oidc is false (default behavior)', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.GH_OIDC_INPUTS,
|
...mocks.GH_OIDC_INPUTS,
|
||||||
'force-skip-oidc': 'false'
|
'force-skip-oidc': 'false',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
@@ -444,12 +454,14 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('works with role chaining when force-skip-oidc is true', async () => {
|
it('works with role chaining when force-skip-oidc is true', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.EXISTING_ROLE_INPUTS,
|
...mocks.EXISTING_ROLE_INPUTS,
|
||||||
'force-skip-oidc': 'true',
|
'force-skip-oidc': 'true',
|
||||||
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
'aws-access-key-id': 'MYAWSACCESSKEYID',
|
||||||
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY'
|
'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||||
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
@@ -472,10 +484,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('succeeds when account ID matches allowed list', async () => {
|
it('succeeds when account ID matches allowed list', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'allowed-account-ids': '111111111111'
|
'allowed-account-ids': '111111111111',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
||||||
@@ -488,10 +502,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('succeeds with multiple allowed account IDs when account matches', async () => {
|
it('succeeds with multiple allowed account IDs when account matches', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'allowed-account-ids': '999999999999,111111111111,222222222222'
|
'allowed-account-ids': '999999999999,111111111111,222222222222',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
||||||
@@ -503,10 +519,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails when account ID does not match allowed list', async () => {
|
it('fails when account ID does not match allowed list', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'allowed-account-ids': '999999999999'
|
'allowed-account-ids': '999999999999',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
||||||
@@ -515,15 +533,17 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
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 () => {
|
it('fails when account ID does not match any in multiple allowed accounts', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'allowed-account-ids': '999999999999,888888888888'
|
'allowed-account-ids': '999999999999,888888888888',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
@@ -533,15 +553,17 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
|
|
||||||
await run();
|
await run();
|
||||||
expect(core.setFailed).toHaveBeenCalledWith(
|
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 () => {
|
it('works with assume role when account ID matches', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_ASSUMEROLE_INPUTS,
|
...mocks.IAM_ASSUMEROLE_INPUTS,
|
||||||
'allowed-account-ids': '111111111111'
|
'allowed-account-ids': '111111111111',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
mockedSTSClient.on(AssumeRoleCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
@@ -555,10 +577,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('works with OIDC when account ID matches', async () => {
|
it('works with OIDC when account ID matches', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.GH_OIDC_INPUTS,
|
...mocks.GH_OIDC_INPUTS,
|
||||||
'allowed-account-ids': '111111111111'
|
'allowed-account-ids': '111111111111',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
|
||||||
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolves(mocks.outputs.STS_CREDENTIALS);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
@@ -570,10 +594,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles GetCallerIdentity API failure gracefully', async () => {
|
it('handles GetCallerIdentity API failure gracefully', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'allowed-account-ids': '111111111111'
|
'allowed-account-ids': '111111111111',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).rejects(new Error('API Error'));
|
mockedSTSClient.on(GetCallerIdentityCommand).rejects(new Error('API Error'));
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
||||||
@@ -585,10 +611,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ignores validation when allowed-account-ids is empty', async () => {
|
it('ignores validation when allowed-account-ids is empty', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'allowed-account-ids': ''
|
'allowed-account-ids': '',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
||||||
@@ -601,10 +629,12 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles whitespace in allowed-account-ids input', async () => {
|
it('handles whitespace in allowed-account-ids input', async () => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput({
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
...mocks.IAM_USER_INPUTS,
|
...mocks.IAM_USER_INPUTS,
|
||||||
'allowed-account-ids': ' 111111111111 , 222222222222 '
|
'allowed-account-ids': ' 111111111111 , 222222222222 ',
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
// biome-ignore lint/suspicious/noExplicitAny: any required to mock private method
|
||||||
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials').mockResolvedValue({
|
||||||
@@ -616,6 +646,86 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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', {}, () => {
|
describe('HTTP Proxy Configuration', {}, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||||
@@ -630,8 +740,8 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
vi.spyOn(core, 'getInput').mockImplementation(
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
mocks.getInput({
|
mocks.getInput({
|
||||||
...mocks.GH_OIDC_INPUTS,
|
...mocks.GH_OIDC_INPUTS,
|
||||||
'http-proxy': 'http://proxy.example.com:8080'
|
'http-proxy': 'http://proxy.example.com:8080',
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
@@ -666,8 +776,8 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
vi.spyOn(core, 'getInput').mockImplementation(
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
mocks.getInput({
|
mocks.getInput({
|
||||||
...mocks.GH_OIDC_INPUTS,
|
...mocks.GH_OIDC_INPUTS,
|
||||||
'http-proxy': 'http://input-proxy.example.com:8080'
|
'http-proxy': 'http://input-proxy.example.com:8080',
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
@@ -682,8 +792,8 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
vi.spyOn(core, 'getInput').mockImplementation(
|
vi.spyOn(core, 'getInput').mockImplementation(
|
||||||
mocks.getInput({
|
mocks.getInput({
|
||||||
...mocks.GH_OIDC_INPUTS,
|
...mocks.GH_OIDC_INPUTS,
|
||||||
'http-proxy': 'http://proxy.example.com:8080'
|
'http-proxy': 'http://proxy.example.com:8080',
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
@@ -691,5 +801,25 @@ describe('Configure AWS Credentials', {}, () => {
|
|||||||
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
|
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
|
||||||
expect(core.setFailed).not.toHaveBeenCalled();
|
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
@@ -3,7 +3,7 @@
|
|||||||
"allowUnreachableCode": false,
|
"allowUnreachableCode": false,
|
||||||
"allowUnusedLabels": false,
|
"allowUnusedLabels": false,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"exactOptionalPropertyTypes": false,
|
"exactOptionalPropertyTypes": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noImplicitOverride": true,
|
"noImplicitOverride": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user