Skip to content

Early eval #380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
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
72 changes: 72 additions & 0 deletions .github/workflows/test-early-eval.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Test OpenTofu early eval

on:
- pull_request

permissions:
contents: read

jobs:
s3-backend:
runs-on: ubuntu-24.04
name: Plan with early eval
permissions:
contents: read
pull-requests: write
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: tofu plan
uses: ./tofu-plan
id: plan
with:
path: tests/workflows/test-early-eval/s3
variables: |
passphrase = "tofuqwertyuiopasdfgh"

- name: Verify outputs
env:
JSON_PLAN_PATH: ${{ steps.plan.outputs.json_plan_path }}
run: |
if [[ ! -f "$JSON_PLAN_PATH" ]]; then
echo "::error:: json_plan_path not set correctly"
exit 1
fi

- name: tofu apply
uses: ./tofu-apply
with:
path: tests/workflows/test-early-eval/s3
variables: |
passphrase = "tofuqwertyuiopasdfgh"

- name: Create workspace
uses: ./tofu-new-workspace
with:
path: tests/workflows/test-early-eval/s3
workspace: test-workspace
variables: |
passphrase = "tofuqwertyuiopasdfgh"

- name: Create workspace again
uses: ./tofu-new-workspace
with:
path: tests/workflows/test-early-eval/s3
workspace: test-workspace
variables: |
passphrase = "tofuqwertyuiopasdfgh"

- name: Destroy workspace
uses: ./tofu-destroy-workspace
with:
path: tests/workflows/test-early-eval/s3
workspace: test-workspace
variables: |
passphrase = "tofuqwertyuiopasdfgh"
5 changes: 4 additions & 1 deletion docs-gen/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Input:
deprecation_message: str = None
show_in_docs: bool = True
example: str = None
available_in: list[Type[Terraform] | Type[OpenTofu]] = dataclasses.field(default_factory=lambda: [Terraform, OpenTofu])

def markdown(self, tool: Tool) -> str:
if self.deprecation_message is None:
Expand Down Expand Up @@ -226,6 +227,8 @@ def markdown(self, tool: Tool) -> str:
for input in self.inputs:
if not input.show_in_docs:
continue
if tool not in input.available_in:
continue
s += text_chunk(input.markdown(tool))

if self.outputs:
Expand Down Expand Up @@ -264,7 +267,7 @@ def action_yaml(self, tool: Tool) -> str:
if self.inputs:
s += 'inputs:\n'

for input in self.inputs:
for input in (input for input in self.inputs if tool in input.available_in):
s += f' {input.name}:\n'

description = input.meta_description or input.description
Expand Down
2 changes: 1 addition & 1 deletion docs-gen/actions/destroy_workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,4 @@
workspace: ${{ github.head_ref }}
```
'''
)
)
11 changes: 9 additions & 2 deletions docs-gen/actions/fmt.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import dataclasses

from action import Action
from action import Action, OpenTofu
from environment_variables.GITHUB_DOT_COM_TOKEN import GITHUB_DOT_COM_TOKEN
from environment_variables.TERRAFORM_CLOUD_TOKENS import TERRAFORM_CLOUD_TOKENS
from inputs.backend_config import backend_config
from inputs.backend_config_file import backend_config_file
from inputs.path import path
from inputs.var_file import var_file
from inputs.variables import variables
from inputs.workspace import workspace

fmt = Action(
Expand All @@ -20,6 +22,11 @@
$ProductName workspace to inspect when discovering the $ProductName version to use, if the version is not otherwise specified.
See [dflook/$ToolName-version](https://github.com/dflook/terraform-github-actions/tree/main/$ToolName-version#$ToolName-version-action) for details.
'''),
dataclasses.replace(variables, available_in=[OpenTofu], description='''
Variables to set when initializing $ProductName. This should be valid $ProductName syntax - like a [variable definition file]($VariableDefinitionUrl).
Variables set here override any given in `var_file`s.
'''),
dataclasses.replace(var_file, available_in=[OpenTofu]),
dataclasses.replace(backend_config, description='''
List of $ProductName backend config values, one per line. This is used for discovering the $ProductName version to use, if the version is not otherwise specified.
See [dflook/$ToolName-version](https://github.com/dflook/terraform-github-actions/tree/main/$ToolName-version#$ToolName-version-action) for details.
Expand Down Expand Up @@ -70,4 +77,4 @@
branch: automated-$ToolName-fmt
```
'''
)
)
11 changes: 9 additions & 2 deletions docs-gen/actions/fmt_check.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import dataclasses

from action import Action
from action import Action, OpenTofu
from environment_variables.GITHUB_DOT_COM_TOKEN import GITHUB_DOT_COM_TOKEN
from environment_variables.TERRAFORM_CLOUD_TOKENS import TERRAFORM_CLOUD_TOKENS
from inputs.backend_config import backend_config
from inputs.backend_config_file import backend_config_file
from inputs.path import path
from inputs.var_file import var_file
from inputs.variables import variables
from inputs.workspace import workspace
from outputs.failure_reason import failure_reason

Expand All @@ -24,6 +26,11 @@
$ProductName workspace to inspect when discovering the $ProductName version to use, if the version is not otherwise specified.
See [dflook/$ToolName-version](https://github.com/dflook/terraform-github-actions/tree/main/$ToolName-version#$ToolName-version-action) for details.
'''),
dataclasses.replace(variables, available_in=[OpenTofu], description='''
Variables to set when initializing $ProductName. This should be valid $ProductName syntax - like a [variable definition file]($VariableDefinitionUrl).
Variables set here override any given in `var_file`s.
'''),
dataclasses.replace(var_file, available_in=[OpenTofu]),
dataclasses.replace(backend_config, description='''
List of $ProductName backend config values, one per line. This is used for discovering the $ProductName version to use, if the version is not otherwise specified.
See [dflook/$ToolName-version](https://github.com/dflook/terraform-github-actions/tree/main/$ToolName-version#$ToolName-version-action) for details.
Expand Down Expand Up @@ -96,4 +103,4 @@
run: echo "formatting check failed"
```
'''
)
)
12 changes: 10 additions & 2 deletions docs-gen/actions/new_workspace.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dataclasses

from action import Action
from action import Action, OpenTofu
from environment_variables.GITHUB_DOT_COM_TOKEN import GITHUB_DOT_COM_TOKEN
from environment_variables.TERRAFORM_CLOUD_TOKENS import TERRAFORM_CLOUD_TOKENS
from environment_variables.TERRAFORM_HTTP_CREDENTIALS import TERRAFORM_HTTP_CREDENTIALS
Expand All @@ -9,6 +9,8 @@
from inputs.backend_config import backend_config
from inputs.backend_config_file import backend_config_file
from inputs.path import path
from inputs.var_file import var_file
from inputs.variables import variables
from inputs.workspace import workspace

new_workspace = Action(
Expand All @@ -19,6 +21,12 @@
inputs=[
path,
dataclasses.replace(workspace, description='The name of the $ProductName workspace to create.', required=True, default=None),
dataclasses.replace(variables, available_in=[OpenTofu], description='''
Variables to set when initializing $ProductName. This should be valid $ProductName syntax - like a [variable definition file]($VariableDefinitionUrl).

Variables set here override any given in `var_file`s.
'''),
dataclasses.replace(var_file, available_in=[OpenTofu]),
backend_config,
backend_config_file,
],
Expand Down Expand Up @@ -62,4 +70,4 @@
auto_approve: true
```
'''
)
)
14 changes: 11 additions & 3 deletions docs-gen/actions/output.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dataclasses

from action import Action
from action import Action, OpenTofu
from environment_variables.GITHUB_DOT_COM_TOKEN import GITHUB_DOT_COM_TOKEN
from environment_variables.TERRAFORM_CLOUD_TOKENS import TERRAFORM_CLOUD_TOKENS
from environment_variables.TERRAFORM_HTTP_CREDENTIALS import TERRAFORM_HTTP_CREDENTIALS
Expand All @@ -9,6 +9,8 @@
from inputs.backend_config import backend_config
from inputs.backend_config_file import backend_config_file
from inputs.path import path
from inputs.var_file import var_file
from inputs.variables import variables
from inputs.workspace import workspace
from outputs.terraform_outputs import terraform_outputs

Expand All @@ -20,8 +22,14 @@
inputs=[
path,
dataclasses.replace(workspace, description='$ProductName workspace to get outputs from'),
dataclasses.replace(variables, available_in=[OpenTofu], description='''
Variables to set when initializing $ProductName. This should be valid $ProductName syntax - like a [variable definition file]($VariableDefinitionUrl).

Variables set here override any given in `var_file`s.
'''),
dataclasses.replace(var_file, available_in=[OpenTofu]),
backend_config,
backend_config_file,
backend_config_file
],
environment_variables=[
GITHUB_DOT_COM_TOKEN,
Expand Down Expand Up @@ -106,4 +114,4 @@
The subnet-ids are subnet-053008016a2c1768c,subnet-07d4ce437c43eba2f,subnet-0a5f8c3a20023b8c0
```
'''
)
)
61 changes: 43 additions & 18 deletions image/actions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,13 @@ function relative_to() {
function init() {
start_group "Initializing $TOOL_PRODUCT_NAME"

set-init-args

rm -rf "$TF_DATA_DIR"
debug_log "$TOOL_COMMAND_NAME" init -input=false -backend=false
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false -backend=false)
# shellcheck disable=SC2086
debug_log "$TOOL_COMMAND_NAME" init -input=false -backend=false $EARLY_VARIABLE_ARGS
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false -backend=false $EARLY_VARIABLE_ARGS)

end_group
}
Expand All @@ -184,14 +188,20 @@ function init() {
function init-test() {
start_group "Initializing $TOOL_PRODUCT_NAME"

set-init-args

rm -rf "$TF_DATA_DIR"

if [[ -n "$INPUT_TEST_DIRECTORY" ]]; then
debug_log "$TOOL_COMMAND_NAME" init -input=false -backend=false -test-directory "$INPUT_TEST_DIRECTORY"
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false -backend=false -test-directory "$INPUT_TEST_DIRECTORY")
# shellcheck disable=SC2086
debug_log "$TOOL_COMMAND_NAME" init -input=false -backend=false $EARLY_VARIABLE_ARGS -test-directory "$INPUT_TEST_DIRECTORY"
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false -backend=false $EARLY_VARIABLE_ARGS -test-directory "$INPUT_TEST_DIRECTORY")
else
debug_log "$TOOL_COMMAND_NAME" init -input=false -backend=false
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false -backend=false)
# shellcheck disable=SC2086
debug_log "$TOOL_COMMAND_NAME" init -input=false -backend=false $EARLY_VARIABLE_ARGS
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false -backend=false $EARLY_VARIABLE_ARGS)
fi

end_group
Expand All @@ -218,6 +228,14 @@ function set-init-args() {
done
fi

if [[ -v OPENTOFU && $TERRAFORM_VER_MINOR -ge 8 ]]; then
debug_log "Preparing variables for early evaluation"
set-variable-args
EARLY_VARIABLE_ARGS=$VARIABLE_ARGS
else
EARLY_VARIABLE_ARGS=""
fi

export INIT_ARGS
}

Expand All @@ -232,12 +250,12 @@ function init-backend-workspace() {

rm -rf "$TF_DATA_DIR"

# shellcheck disable=SC2016
debug_log TF_WORKSPACE="$INPUT_WORKSPACE" "$TOOL_COMMAND_NAME" init -input=false '$INIT_ARGS' # don't expand INIT_ARGS
# shellcheck disable=SC2016,SC2086
debug_log TF_WORKSPACE="$INPUT_WORKSPACE" "$TOOL_COMMAND_NAME" init -input=false '$INIT_ARGS' $EARLY_VARIABLE_ARGS # don't expand INIT_ARGS

set +e
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && TF_WORKSPACE=$INPUT_WORKSPACE $TOOL_COMMAND_NAME init -input=false $INIT_ARGS \
(cd "$INPUT_PATH" && TF_WORKSPACE=$INPUT_WORKSPACE $TOOL_COMMAND_NAME init -input=false $INIT_ARGS $EARLY_VARIABLE_ARGS \
2>"$STEP_TMP_DIR/terraform_init.stderr")

local INIT_EXIT=$?
Expand Down Expand Up @@ -273,11 +291,11 @@ function init-backend-default-workspace() {

rm -rf "$TF_DATA_DIR"

# shellcheck disable=SC2016
debug_log "$TOOL_COMMAND_NAME" init -input=false '$INIT_ARGS' # don't expand INIT_ARGS
# shellcheck disable=SC2016,SC2086
debug_log "$TOOL_COMMAND_NAME" init -input=false '$INIT_ARGS' $EARLY_VARIABLE_ARGS # don't expand INIT_ARGS
set +e
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false $INIT_ARGS \
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME init -input=false $INIT_ARGS $EARLY_VARIABLE_ARGS \
2>"$STEP_TMP_DIR/terraform_init.stderr")

local INIT_EXIT=$?
Expand All @@ -302,9 +320,12 @@ function init-backend-default-workspace() {
function select-workspace() {
local WORKSPACE_EXIT

debug_log "$TOOL_COMMAND_NAME" workspace select "$INPUT_WORKSPACE"
# shellcheck disable=SC2086
debug_log "$TOOL_COMMAND_NAME" workspace select $EARLY_VARIABLE_ARGS "$INPUT_WORKSPACE"

set +e
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME workspace select "$INPUT_WORKSPACE") >"$STEP_TMP_DIR/workspace_select" 2>&1
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && "$TOOL_COMMAND_NAME" workspace select $EARLY_VARIABLE_ARGS "$INPUT_WORKSPACE") >"$STEP_TMP_DIR/workspace_select" 2>&1
WORKSPACE_EXIT=$?
set -e

Expand Down Expand Up @@ -438,8 +459,10 @@ function set-remote-plan-args() {
}

function output() {
debug_log "$TOOL_COMMAND_NAME" output -json
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME output -json | tee "$STEP_TMP_DIR/terraform_output.json" | convert_output)
# shellcheck disable=SC2086
debug_log "$TOOL_COMMAND_NAME" output -json $EARLY_VARIABLE_ARGS
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME output -json $EARLY_VARIABLE_ARGS | tee "$STEP_TMP_DIR/terraform_output.json" | convert_output)
}

function random_string() {
Expand Down Expand Up @@ -535,8 +558,10 @@ function destroy() {

function force_unlock() {
echo "Unlocking state with ID: $INPUT_LOCK_ID"
debug_log "$TOOL_COMMAND_NAME" force-unlock -force "$INPUT_LOCK_ID"
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME force-unlock -force "$INPUT_LOCK_ID")
# shellcheck disable=SC2086
debug_log "$TOOL_COMMAND_NAME" force-unlock -force $EARLY_VARIABLE_ARGS "$INPUT_LOCK_ID"
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME force-unlock -force $EARLY_VARIABLE_ARGS "$INPUT_LOCK_ID")
}

# Every file written to disk should use one of these directories
Expand Down
6 changes: 4 additions & 2 deletions image/entrypoints/destroy-workspace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ else
# We can't delete an active workspace, so re-initialize with a 'default' workspace (which may not exist)
init-backend-default-workspace

debug_log terraform workspace delete -no-color -lock-timeout=300s "$INPUT_WORKSPACE"
(cd "$INPUT_PATH" && terraform workspace delete -no-color -lock-timeout=300s "$INPUT_WORKSPACE")
# shellcheck disable=SC2086
debug_log $TOOL_COMMAND_NAME workspace delete $EARLY_VARIABLE_ARGS -no-color -lock-timeout=300s "$INPUT_WORKSPACE"
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && $TOOL_COMMAND_NAME workspace delete $EARLY_VARIABLE_ARGS -no-color -lock-timeout=300s "$INPUT_WORKSPACE")
fi
Loading
Loading