Skip to content

Commit 6fe20d9

Browse files
committed
Add json_output_path
1 parent 403978d commit 6fe20d9

File tree

20 files changed

+396
-1
lines changed

20 files changed

+396
-1
lines changed

.github/workflows/test-output.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
MY_OBJECT_FIRST: ${{ fromJson(steps.terraform-output.outputs.my_object).first }}
4242
MY_TUPLE: ${{ join(fromJson(steps.terraform-output.outputs.my_tuple)) }}
4343
MY_SET: ${{ contains(fromJson(steps.terraform-output.outputs.my_set), 'one') }}
44+
JSON_OUTPUT_PATH: ${{ steps.terraform-output.outputs.json_output_path }}
4445
run: |
4546
if [[ "$MY_NUMBER" != "5" ]]; then
4647
echo "::error:: output my_number not set correctly"
@@ -114,3 +115,56 @@ jobs:
114115
echo "::error:: steps.terraform-output.outputs.my_multiline_string not set correctly"
115116
exit 1
116117
fi
118+
119+
## Check if the JSON output file exists and validate its contents
120+
121+
if [[ ! -f "$JSON_OUTPUT_PATH" ]]; then
122+
echo "::error:: JSON output file not found at $JSON_OUTPUT_PATH"
123+
exit 1
124+
fi
125+
126+
# Parse JSON and validate primitive types
127+
JSON_MY_NUMBER=$(jq -r '.my_number' "$JSON_OUTPUT_PATH")
128+
if [[ "$JSON_MY_NUMBER" != "5" ]]; then
129+
echo "::error:: JSON my_number should be 5, got: $JSON_MY_NUMBER"
130+
exit 1
131+
fi
132+
133+
JSON_MY_STRING=$(jq -r '.my_string' "$JSON_OUTPUT_PATH")
134+
if [[ "$JSON_MY_STRING" != "hello" ]]; then
135+
echo "::error:: JSON my_string should be 'hello', got: $JSON_MY_STRING"
136+
exit 1
137+
fi
138+
139+
JSON_MY_BOOL=$(jq -r '.my_bool' "$JSON_OUTPUT_PATH")
140+
if [[ "$JSON_MY_BOOL" != "true" ]]; then
141+
echo "::error:: JSON my_bool should be true, got: $JSON_MY_BOOL"
142+
exit 1
143+
fi
144+
145+
# Validate sensitive values are included in JSON
146+
JSON_MY_SENSITIVE_NUMBER=$(jq -r '.my_sensitive_number' "$JSON_OUTPUT_PATH")
147+
if [[ "$JSON_MY_SENSITIVE_NUMBER" != "6" ]]; then
148+
echo "::error:: JSON my_sensitive_number should be 6, got: $JSON_MY_SENSITIVE_NUMBER"
149+
exit 1
150+
fi
151+
152+
# Validate list becomes array (preserves order)
153+
JSON_MY_LIST=$(jq -c '.my_list' "$JSON_OUTPUT_PATH")
154+
# List from tolist(toset()) may have different order, so check elements exist
155+
JSON_MY_LIST_HAS_ONE=$(jq -r '.my_list | contains(["one"])' "$JSON_OUTPUT_PATH")
156+
JSON_MY_LIST_HAS_TWO=$(jq -r '.my_list | contains(["two"])' "$JSON_OUTPUT_PATH")
157+
JSON_MY_LIST_LENGTH=$(jq -r '.my_list | length' "$JSON_OUTPUT_PATH")
158+
if [[ "$JSON_MY_LIST_HAS_ONE" != "true" || "$JSON_MY_LIST_HAS_TWO" != "true" || "$JSON_MY_LIST_LENGTH" != "2" ]]; then
159+
echo "::error:: JSON my_list should contain 'one' and 'two' with length 2"
160+
exit 1
161+
fi
162+
163+
# Validate map/object becomes JSON object
164+
JSON_MY_MAP_FIRST=$(jq -r '.my_map.first' "$JSON_OUTPUT_PATH")
165+
JSON_MY_MAP_SECOND=$(jq -r '.my_map.second' "$JSON_OUTPUT_PATH")
166+
JSON_MY_MAP_THIRD=$(jq -r '.my_map.third' "$JSON_OUTPUT_PATH")
167+
if [[ "$JSON_MY_MAP_FIRST" != "one" || "$JSON_MY_MAP_SECOND" != "two" || "$JSON_MY_MAP_THIRD" != "3" ]]; then
168+
echo "::error:: JSON my_map should have correct key-value pairs"
169+
exit 1
170+
fi

docs-gen/action.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ def assert_ordering(self):
195195
"terraform",
196196
"tofu",
197197
"Provider Versions",
198+
"json_output_path",
198199
"$ProductName Outputs",
199200
], self.outputs)
200201

docs-gen/actions/apply.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from inputs.var import var
2626
from inputs.workspace import workspace
2727
from outputs.failure_reason import failure_reason
28+
from outputs.json_output_path import json_output_path
2829
from outputs.json_plan_path import json_plan_path
2930
from outputs.lock_info import lock_info
3031
from outputs.run_id import run_id
@@ -112,6 +113,7 @@
112113
),
113114
lock_info,
114115
run_id,
116+
json_output_path,
115117
terraform_outputs
116118
],
117119
environment_variables=[

docs-gen/actions/output.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from inputs.var_file import var_file
1313
from inputs.variables import variables
1414
from inputs.workspace import workspace
15+
from outputs.json_output_path import json_output_path
1516
from outputs.terraform_outputs import terraform_outputs
1617

1718
output = Action(
@@ -39,6 +40,7 @@
3940
TERRAFORM_PRE_RUN
4041
],
4142
outputs=[
43+
json_output_path,
4244
terraform_outputs
4345
],
4446
extra='''

docs-gen/actions/remote_state.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from inputs.backend_config_file import backend_config_file
88
from inputs.backend_type import backend_type
99
from inputs.workspace import workspace
10+
from outputs.json_output_path import json_output_path
1011
from outputs.terraform_outputs import terraform_outputs
1112

1213
remote_state = Action(
@@ -21,6 +22,7 @@
2122
backend_config_file,
2223
],
2324
outputs=[
25+
json_output_path,
2426
terraform_outputs
2527
],
2628
environment_variables=[
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from action import Output
2+
3+
json_output_path = Output(
4+
name='json_output_path',
5+
type='string',
6+
description='''
7+
This is the path to all the root module outputs in a JSON file.
8+
The path is relative to the Actions workspace.
9+
10+
For example, with the $ProductName config:
11+
12+
```hcl
13+
output "service_hostname" {
14+
value = "example.com"
15+
}
16+
```
17+
18+
The file pointed to by this output will contain:
19+
20+
```json
21+
{
22+
"service_hostname": "example.com"
23+
}
24+
```
25+
26+
$ProductName list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.
27+
'''
28+
)

image/actions.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,11 @@ function output() {
443443
# shellcheck disable=SC2086
444444
debug_log "$TOOL_COMMAND_NAME" output -json
445445
# shellcheck disable=SC2086
446-
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME output -json | tee "$STEP_TMP_DIR/terraform_output.json" | convert_output)
446+
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME output -json | tee "$STEP_TMP_DIR/terraform_output.json" | convert_output "$STEP_TMP_DIR/outputs.json")
447+
448+
mkdir -p "$GITHUB_WORKSPACE/$WORKSPACE_TMP_DIR"
449+
cp "$STEP_TMP_DIR/outputs.json" "$GITHUB_WORKSPACE/$WORKSPACE_TMP_DIR/outputs.json"
450+
set_output json_output_path "$WORKSPACE_TMP_DIR/outputs.json"
447451
}
448452

449453
function random_string() {

image/tools/convert_output.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
import sys
55
from dataclasses import dataclass
6+
from pathlib import Path
67
from typing import Dict, Iterable, Union
78
from github_actions.commands import output, mask
89

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

59+
def write_json_outputs(json_output_path: Path, outputs: Dict):
60+
"""
61+
Flatten the terraform json output format to a simple dictionary, and write it to a file.
62+
"""
63+
64+
json_outputs = {name: o['value'] for name, o in outputs.items()}
65+
66+
with open(json_output_path, 'w') as f:
67+
json.dump(json_outputs, f, indent=2, sort_keys=True)
5868

5969
if __name__ == '__main__':
6070

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

80+
write_json_outputs(Path(sys.argv[1]), outputs)
81+
7082
for command in convert_to_github(outputs):
7183
if isinstance(command, Output):
7284
output(command.name, command.value)

terraform-apply/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,31 @@ These input values must be the same as any [`dflook/terraform-plan`](https://git
269269

270270
- Type: string
271271

272+
* `json_output_path`
273+
274+
This is the path to all the root module outputs in a JSON file.
275+
The path is relative to the Actions workspace.
276+
277+
For example, with the Terraform config:
278+
279+
```hcl
280+
output "service_hostname" {
281+
value = "example.com"
282+
}
283+
```
284+
285+
The file pointed to by this output will contain:
286+
287+
```json
288+
{
289+
"service_hostname": "example.com"
290+
}
291+
```
292+
293+
Terraform list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.
294+
295+
- Type: string
296+
272297
* Terraform Outputs
273298

274299
An action output will be created for each output of the Terraform configuration.

terraform-apply/action.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,28 @@ outputs:
140140
```
141141
run_id:
142142
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.
143+
json_output_path:
144+
description: |
145+
This is the path to all the root module outputs in a JSON file.
146+
The path is relative to the Actions workspace.
147+
148+
For example, with the Terraform config:
149+
150+
```hcl
151+
output "service_hostname" {
152+
value = "example.com"
153+
}
154+
```
155+
156+
The file pointed to by this output will contain:
157+
158+
```json
159+
{
160+
"service_hostname": "example.com"
161+
}
162+
```
163+
164+
Terraform list, set and tuple types are cast to a JSON array, map and object types are cast to a JSON object.
143165
144166
runs:
145167
using: docker

0 commit comments

Comments
 (0)