Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/test-output.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
MY_OBJECT_FIRST: ${{ fromJson(steps.terraform-output.outputs.my_object).first }}
MY_TUPLE: ${{ join(fromJson(steps.terraform-output.outputs.my_tuple)) }}
MY_SET: ${{ contains(fromJson(steps.terraform-output.outputs.my_set), 'one') }}
JSON_OUTPUT_PATH: ${{ steps.terraform-output.outputs.json_output_path }}
run: |
if [[ "$MY_NUMBER" != "5" ]]; then
echo "::error:: output my_number not set correctly"
Expand Down Expand Up @@ -114,3 +115,56 @@ jobs:
echo "::error:: steps.terraform-output.outputs.my_multiline_string not set correctly"
exit 1
fi

## Check if the JSON output file exists and validate its contents

cat "$JSON_OUTPUT_PATH"

if [[ ! -f "$JSON_OUTPUT_PATH" ]]; then
echo "::error:: JSON output file not found at $JSON_OUTPUT_PATH"
exit 1
fi

# Parse JSON and validate primitive types
JSON_MY_NUMBER=$(jq -r '.my_number' "$JSON_OUTPUT_PATH")
if [[ "$JSON_MY_NUMBER" != "5" ]]; then
echo "::error:: JSON my_number should be 5, got: $JSON_MY_NUMBER"
exit 1
fi

JSON_MY_STRING=$(jq -r '.my_string' "$JSON_OUTPUT_PATH")
if [[ "$JSON_MY_STRING" != "hello" ]]; then
echo "::error:: JSON my_string should be 'hello', got: $JSON_MY_STRING"
exit 1
fi

JSON_MY_BOOL=$(jq -r '.my_bool' "$JSON_OUTPUT_PATH")
if [[ "$JSON_MY_BOOL" != "true" ]]; then
echo "::error:: JSON my_bool should be true, got: $JSON_MY_BOOL"
exit 1
fi

# Validate sensitive values are included in JSON
JSON_MY_SENSITIVE_NUMBER=$(jq -r '.my_sensitive_number' "$JSON_OUTPUT_PATH")
if [[ "$JSON_MY_SENSITIVE_NUMBER" != "6" ]]; then
echo "::error:: JSON my_sensitive_number should be 6, got: $JSON_MY_SENSITIVE_NUMBER"
exit 1
fi

# List from tolist(toset()) may have different order, so check elements exist
JSON_MY_LIST_HAS_ONE=$(jq -r '.my_list | contains(["one"])' "$JSON_OUTPUT_PATH")
JSON_MY_LIST_HAS_TWO=$(jq -r '.my_list | contains(["two"])' "$JSON_OUTPUT_PATH")
JSON_MY_LIST_LENGTH=$(jq -r '.my_list | length' "$JSON_OUTPUT_PATH")
if [[ "$JSON_MY_LIST_HAS_ONE" != "true" || "$JSON_MY_LIST_HAS_TWO" != "true" || "$JSON_MY_LIST_LENGTH" != "2" ]]; then
echo "::error:: JSON my_list should contain 'one' and 'two' with length 2"
exit 1
fi

# Validate map/object becomes JSON object
JSON_MY_MAP_FIRST=$(jq -r '.my_map.first' "$JSON_OUTPUT_PATH")
JSON_MY_MAP_SECOND=$(jq -r '.my_map.second' "$JSON_OUTPUT_PATH")
JSON_MY_MAP_THIRD=$(jq -r '.my_map.third' "$JSON_OUTPUT_PATH")
if [[ "$JSON_MY_MAP_FIRST" != "one" || "$JSON_MY_MAP_SECOND" != "two" || "$JSON_MY_MAP_THIRD" != "3" ]]; then
echo "::error:: JSON my_map should have correct key-value pairs"
exit 1
fi
18 changes: 16 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,23 @@ The actions are versioned as a suite. Some actions may have no change in behavio

When using an action you can specify the version as:

- `@v2.1.0` to use an exact release
- `@v2.1` to use the latest patch release for the specific minor version
- `@v2.2.0` to use an exact release
- `@v2.2` to use the latest patch release for the specific minor version
- `@v2` to use the latest patch release for the specific major version

## [2.2.0] - 2025-08-01

### Added
- New `json_output_path` output for [dflook/terraform-apply](https://github.com/dflook/terraform-github-actions/tree/main/terraform-apply),
[dflook/terraform-output](https://github.com/dflook/terraform-github-actions/tree/main/terraform-output),
[dflook/terraform-remote-state](https://github.com/dflook/terraform-github-actions/tree/main/terraform-remote-state),
[dflook/tofu-apply](https://github.com/dflook/terraform-github-actions/tree/main/tofu-apply),
[dflook/tofu-output](https://github.com/dflook/terraform-github-actions/tree/main/tofu-output), and
[dflook/tofu-remote-state](https://github.com/dflook/terraform-github-actions/tree/main/tofu-remote-state) actions.

The `json_output_path` output points to a JSON file containing all root module outputs in a simple key-value format.
This addresses GitHub Actions' output size limitations and enables easier access to all outputs as a single object.

## [2.1.0] - 2025-06-16

### Added
Expand Down Expand Up @@ -772,6 +785,7 @@ First release of the GitHub Actions:
- [dflook/terraform-new-workspace](terraform-new-workspace)
- [dflook/terraform-destroy-workspace](terraform-destroy-workspace)

[2.2.0]: https://github.com/dflook/terraform-github-actions/compare/v2.1.0...v2.2.0
[2.1.0]: https://github.com/dflook/terraform-github-actions/compare/v2.0.1...v2.1.0
[2.0.1]: https://github.com/dflook/terraform-github-actions/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/dflook/terraform-github-actions/compare/v1.49.0...v2.0.0
Expand Down
1 change: 1 addition & 0 deletions docs-gen/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def assert_ordering(self):
"terraform",
"tofu",
"Provider Versions",
"json_output_path",
"$ProductName Outputs",
], self.outputs)

Expand Down
2 changes: 2 additions & 0 deletions docs-gen/actions/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from inputs.var import var
from inputs.workspace import workspace
from outputs.failure_reason import failure_reason
from outputs.json_output_path import json_output_path
from outputs.json_plan_path import json_plan_path
from outputs.lock_info import lock_info
from outputs.run_id import run_id
Expand Down Expand Up @@ -112,6 +113,7 @@
),
lock_info,
run_id,
json_output_path,
terraform_outputs
],
environment_variables=[
Expand Down
2 changes: 2 additions & 0 deletions docs-gen/actions/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from inputs.var_file import var_file
from inputs.variables import variables
from inputs.workspace import workspace
from outputs.json_output_path import json_output_path
from outputs.terraform_outputs import terraform_outputs

output = Action(
Expand Down Expand Up @@ -39,6 +40,7 @@
TERRAFORM_PRE_RUN
],
outputs=[
json_output_path,
terraform_outputs
],
extra='''
Expand Down
2 changes: 2 additions & 0 deletions docs-gen/actions/remote_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from inputs.backend_config_file import backend_config_file
from inputs.backend_type import backend_type
from inputs.workspace import workspace
from outputs.json_output_path import json_output_path
from outputs.terraform_outputs import terraform_outputs

remote_state = Action(
Expand All @@ -21,6 +22,7 @@
backend_config_file,
],
outputs=[
json_output_path,
terraform_outputs
],
environment_variables=[
Expand Down
28 changes: 28 additions & 0 deletions docs-gen/outputs/json_output_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from action import Output

json_output_path = Output(
name='json_output_path',
type='string',
description='''
This is the path to all the root module outputs in a JSON file.
The path is relative to the Actions workspace.
For example, with the $ProductName config:
```hcl
output "service_hostname" {
value = "example.com"
}
```
The file pointed to by this output will contain:
```json
{
"service_hostname": "example.com"
}
```
$ProductName list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.
'''
)
6 changes: 5 additions & 1 deletion image/actions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,11 @@ function output() {
# shellcheck disable=SC2086
debug_log "$TOOL_COMMAND_NAME" output -json
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME output -json | tee "$STEP_TMP_DIR/terraform_output.json" | convert_output)
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME output -json | tee "$STEP_TMP_DIR/terraform_output.json" | convert_output "$STEP_TMP_DIR/outputs.json")

mkdir -p "$GITHUB_WORKSPACE/$WORKSPACE_TMP_DIR"
cp "$STEP_TMP_DIR/outputs.json" "$GITHUB_WORKSPACE/$WORKSPACE_TMP_DIR/outputs.json"
set_output json_output_path "$WORKSPACE_TMP_DIR/outputs.json"
}

function random_string() {
Expand Down
12 changes: 12 additions & 0 deletions image/tools/convert_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterable, Union
from github_actions.commands import output, mask

Expand Down Expand Up @@ -55,6 +56,15 @@ def read_input(s: str) -> dict:
jstr = '\n'.join(lines)
return json.loads(jstr)

def write_json_outputs(json_output_path: Path, outputs: Dict):
"""
Flatten the terraform json output format to a simple dictionary, and write it to a file.
"""

json_outputs = {name: o['value'] for name, o in outputs.items()}

with open(json_output_path, 'w') as f:
json.dump(json_outputs, f, indent=2, sort_keys=True)

if __name__ == '__main__':

Expand All @@ -67,6 +77,8 @@ def read_input(s: str) -> dict:
sys.stderr.write(input_string)
raise

write_json_outputs(Path(sys.argv[1]), outputs)

for command in convert_to_github(outputs):
if isinstance(command, Output):
output(command.name, command.value)
Expand Down
25 changes: 25 additions & 0 deletions terraform-apply/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,31 @@ These input values must be the same as any [`dflook/terraform-plan`](https://git

- Type: string

* `json_output_path`

This is the path to all the root module outputs in a JSON file.
The path is relative to the Actions workspace.

For example, with the Terraform config:

```hcl
output "service_hostname" {
value = "example.com"
}
```

The file pointed to by this output will contain:

```json
{
"service_hostname": "example.com"
}
```

Terraform list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.

- Type: string

* Terraform Outputs

An action output will be created for each output of the Terraform configuration.
Expand Down
22 changes: 22 additions & 0 deletions terraform-apply/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,28 @@ outputs:
```
run_id:
description: If the root module uses the `remote` or `cloud` backend in remote execution mode, this output will be set to the remote run id.
json_output_path:
description: |
This is the path to all the root module outputs in a JSON file.
The path is relative to the Actions workspace.
For example, with the Terraform config:
```hcl
output "service_hostname" {
value = "example.com"
}
```
The file pointed to by this output will contain:
```json
{
"service_hostname": "example.com"
}
```
Terraform list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.
runs:
using: docker
Expand Down
25 changes: 25 additions & 0 deletions terraform-output/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ Retrieve the root-level outputs from a Terraform configuration.

## Outputs

* `json_output_path`

This is the path to all the root module outputs in a JSON file.
The path is relative to the Actions workspace.

For example, with the Terraform config:

```hcl
output "service_hostname" {
value = "example.com"
}
```

The file pointed to by this output will contain:

```json
{
"service_hostname": "example.com"
}
```

Terraform list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.

- Type: string

* Terraform Outputs

An action output will be created for each output of the Terraform configuration.
Expand Down
24 changes: 24 additions & 0 deletions terraform-output/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ inputs:
required: false
default: ""

outputs:
json_output_path:
description: |
This is the path to all the root module outputs in a JSON file.
The path is relative to the Actions workspace.
For example, with the Terraform config:
```hcl
output "service_hostname" {
value = "example.com"
}
```
The file pointed to by this output will contain:
```json
{
"service_hostname": "example.com"
}
```
Terraform list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.
runs:
using: docker
image: ../image/Dockerfile
Expand Down
25 changes: 25 additions & 0 deletions terraform-remote-state/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,31 @@ Retrieves the root-level outputs from a Terraform remote state.

## Outputs

* `json_output_path`

This is the path to all the root module outputs in a JSON file.
The path is relative to the Actions workspace.

For example, with the Terraform config:

```hcl
output "service_hostname" {
value = "example.com"
}
```

The file pointed to by this output will contain:

```json
{
"service_hostname": "example.com"
}
```

Terraform list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.

- Type: string

* Terraform Outputs

An action output will be created for each output of the Terraform configuration.
Expand Down
Loading
Loading