ftl2 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ftl2/__init__.py +18 -0
- ftl2/arguments.py +116 -0
- ftl2/automation/__init__.py +275 -0
- ftl2/automation/context.py +1769 -0
- ftl2/automation/proxy.py +1292 -0
- ftl2/backup.py +640 -0
- ftl2/builder.py +121 -0
- ftl2/cli.py +2127 -0
- ftl2/config_profiles.py +258 -0
- ftl2/events.py +226 -0
- ftl2/exceptions.py +375 -0
- ftl2/executor.py +366 -0
- ftl2/ftl_gate/__init__.py +7 -0
- ftl2/ftl_gate/__main__.py +1125 -0
- ftl2/ftl_modules/__init__.py +162 -0
- ftl2/ftl_modules/aws/__init__.py +8 -0
- ftl2/ftl_modules/aws/ec2.py +33 -0
- ftl2/ftl_modules/command.py +141 -0
- ftl2/ftl_modules/exceptions.py +57 -0
- ftl2/ftl_modules/executor.py +521 -0
- ftl2/ftl_modules/file.py +372 -0
- ftl2/ftl_modules/http.py +338 -0
- ftl2/ftl_modules/pip.py +166 -0
- ftl2/ftl_modules/swap.py +230 -0
- ftl2/ftl_modules/wait_for.py +115 -0
- ftl2/gate.py +496 -0
- ftl2/host_filter.py +187 -0
- ftl2/inventory.py +422 -0
- ftl2/logging.py +403 -0
- ftl2/message.py +239 -0
- ftl2/module_docs.py +562 -0
- ftl2/module_loading/__init__.py +75 -0
- ftl2/module_loading/bundle.py +499 -0
- ftl2/module_loading/dependencies.py +508 -0
- ftl2/module_loading/excluded.py +189 -0
- ftl2/module_loading/executor.py +933 -0
- ftl2/module_loading/fqcn.py +421 -0
- ftl2/module_loading/requirements.py +430 -0
- ftl2/module_loading/shadowed.py +58 -0
- ftl2/modules/copy.py +146 -0
- ftl2/modules/file.py +161 -0
- ftl2/modules/ping.py +49 -0
- ftl2/modules/setup.py +80 -0
- ftl2/modules/shell.py +92 -0
- ftl2/policy.py +177 -0
- ftl2/progress.py +631 -0
- ftl2/refs.py +174 -0
- ftl2/retry.py +442 -0
- ftl2/runners.py +1302 -0
- ftl2/safety.py +219 -0
- ftl2/ssh.py +623 -0
- ftl2/state/__init__.py +41 -0
- ftl2/state/execution.py +304 -0
- ftl2/state/file.py +93 -0
- ftl2/state/merge.py +64 -0
- ftl2/state/state.py +288 -0
- ftl2/telemetry.py +73 -0
- ftl2/types.py +311 -0
- ftl2/utils.py +186 -0
- ftl2/vars.py +373 -0
- ftl2/vault.py +99 -0
- ftl2/workflow.py +314 -0
- ftl2-0.1.0.dist-info/METADATA +207 -0
- ftl2-0.1.0.dist-info/RECORD +66 -0
- ftl2-0.1.0.dist-info/WHEEL +4 -0
- ftl2-0.1.0.dist-info/entry_points.txt +3 -0
ftl2/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""FTL2 - Refactored Faster Than Light automation framework.
|
|
2
|
+
|
|
3
|
+
A high-performance automation framework built with modern Python patterns,
|
|
4
|
+
using dataclasses and composition for clean architecture that's portable to Go.
|
|
5
|
+
|
|
6
|
+
Quick Start:
|
|
7
|
+
from ftl2 import automation
|
|
8
|
+
|
|
9
|
+
async with automation() as ftl:
|
|
10
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
11
|
+
await ftl.command(cmd="echo hello")
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
from ftl2.automation import automation, AutomationContext
|
|
17
|
+
|
|
18
|
+
__all__ = ["__version__", "automation", "AutomationContext"]
|
ftl2/arguments.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Argument merging and resolution for module execution.
|
|
2
|
+
|
|
3
|
+
This module provides argument handling logic that combines base module arguments
|
|
4
|
+
with host-specific overrides, resolving any variable references in the process.
|
|
5
|
+
|
|
6
|
+
Key Features:
|
|
7
|
+
- Merge base module arguments with host-specific overrides
|
|
8
|
+
- Resolve Ref objects against host data
|
|
9
|
+
- Precedence: host_args > dereferenced refs > literals
|
|
10
|
+
- Type-safe with dataclasses
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from ftl2.refs import Ref, deref
|
|
17
|
+
from ftl2.types import HostConfig
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ArgumentConfig:
|
|
22
|
+
"""Configuration for module argument handling.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
module_args: Base arguments to pass to all modules. Can contain Ref
|
|
26
|
+
objects for dynamic variable resolution.
|
|
27
|
+
host_args: Mapping of host names to host-specific argument overrides.
|
|
28
|
+
These have higher precedence than module_args.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
module_args: dict[str, Any] = field(default_factory=dict)
|
|
32
|
+
host_args: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def has_refs(args: dict[str, Any] | None) -> bool:
|
|
36
|
+
"""Check if an argument dictionary contains any Ref objects.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
args: Dictionary to check for Ref objects
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
True if any value in the dictionary is a Ref object
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> config = Ref(None, "config")
|
|
46
|
+
>>> has_refs({"src": "/tmp", "dest": "/var"})
|
|
47
|
+
False
|
|
48
|
+
>>> has_refs({"src": config.src_dir, "dest": "/var"})
|
|
49
|
+
True
|
|
50
|
+
"""
|
|
51
|
+
if not args:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
return any(isinstance(value, Ref) for value in args.values())
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def merge_arguments(
|
|
58
|
+
host: HostConfig,
|
|
59
|
+
module_args: dict[str, Any] | None,
|
|
60
|
+
host_args: dict[str, dict[str, Any]] | None,
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
"""Merge module arguments with host-specific overrides.
|
|
63
|
+
|
|
64
|
+
This function implements the argument merging logic with proper precedence:
|
|
65
|
+
1. host_args (highest precedence)
|
|
66
|
+
2. dereferenced refs from module_args
|
|
67
|
+
3. literal values from module_args
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
host: Host configuration to use for dereferencing
|
|
71
|
+
module_args: Base arguments for all hosts
|
|
72
|
+
host_args: Host-specific argument overrides
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Merged and resolved argument dictionary
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
>>> host = HostConfig(
|
|
79
|
+
... name="web1",
|
|
80
|
+
... ansible_host="192.168.1.100",
|
|
81
|
+
... config={"src_dir": "/opt/app"}
|
|
82
|
+
... )
|
|
83
|
+
>>> config = Ref(None, "config")
|
|
84
|
+
>>> module_args = {"src": config.src_dir, "mode": "0755"}
|
|
85
|
+
>>> host_args = {"web1": {"dest": "/var/app"}}
|
|
86
|
+
>>> merge_arguments(host, module_args, host_args)
|
|
87
|
+
{'src': '/opt/app', 'mode': '0755', 'dest': '/var/app'}
|
|
88
|
+
"""
|
|
89
|
+
# Get host-specific overrides for this host
|
|
90
|
+
host_specific_args = {}
|
|
91
|
+
if host_args:
|
|
92
|
+
host_specific_args = host_args.get(host.name, {})
|
|
93
|
+
|
|
94
|
+
# Check if we need to do any merging
|
|
95
|
+
has_refs_in_args = has_refs(module_args)
|
|
96
|
+
|
|
97
|
+
# Fast path: no host-specific args and no refs
|
|
98
|
+
if not host_specific_args and not has_refs_in_args:
|
|
99
|
+
return module_args or {}
|
|
100
|
+
|
|
101
|
+
# Slow path: need to merge and/or resolve refs
|
|
102
|
+
merged_args = {}
|
|
103
|
+
|
|
104
|
+
# Start with module_args (if any)
|
|
105
|
+
if module_args:
|
|
106
|
+
merged_args = module_args.copy()
|
|
107
|
+
|
|
108
|
+
# Resolve any refs (refs have lower precedence than host-specific args)
|
|
109
|
+
if has_refs_in_args:
|
|
110
|
+
for arg_name, arg_value in module_args.items():
|
|
111
|
+
merged_args[arg_name] = deref(host.vars, arg_value)
|
|
112
|
+
|
|
113
|
+
# Apply host-specific args (higher precedence)
|
|
114
|
+
merged_args.update(host_specific_args)
|
|
115
|
+
|
|
116
|
+
return merged_args
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""FTL2 Automation Context Manager.
|
|
2
|
+
|
|
3
|
+
Provides a clean, AI-friendly interface for automation scripts:
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from ftl2.automation import automation
|
|
7
|
+
|
|
8
|
+
async def main():
|
|
9
|
+
async with automation() as ftl:
|
|
10
|
+
await ftl.file(path="/tmp/test", state="directory")
|
|
11
|
+
await ftl.copy(src="config.yml", dest="/etc/app/config.yml")
|
|
12
|
+
response = await ftl.uri(url="https://api.example.com/health")
|
|
13
|
+
|
|
14
|
+
asyncio.run(main())
|
|
15
|
+
|
|
16
|
+
The context manager provides:
|
|
17
|
+
- Clean ftl.module_name() syntax
|
|
18
|
+
- Automatic module discovery
|
|
19
|
+
- Check mode (dry-run) support
|
|
20
|
+
- Execution result tracking
|
|
21
|
+
- 250x faster than subprocess execution
|
|
22
|
+
|
|
23
|
+
This module is designed for AI-generated automation scripts where
|
|
24
|
+
readability and natural language patterns are important.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from contextlib import asynccontextmanager
|
|
28
|
+
from typing import Any, AsyncGenerator, Callable
|
|
29
|
+
|
|
30
|
+
from ftl2.automation.context import (
|
|
31
|
+
AutomationContext,
|
|
32
|
+
AutomationError,
|
|
33
|
+
OutputMode,
|
|
34
|
+
EventCallback,
|
|
35
|
+
)
|
|
36
|
+
from ftl2.automation.proxy import (
|
|
37
|
+
ModuleProxy,
|
|
38
|
+
NamespaceProxy,
|
|
39
|
+
HostScopedProxy,
|
|
40
|
+
HostScopedModuleProxy,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"automation",
|
|
45
|
+
"AutomationContext",
|
|
46
|
+
"AutomationError",
|
|
47
|
+
"ModuleProxy",
|
|
48
|
+
"NamespaceProxy",
|
|
49
|
+
"HostScopedProxy",
|
|
50
|
+
"HostScopedModuleProxy",
|
|
51
|
+
"OutputMode",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@asynccontextmanager
|
|
56
|
+
async def automation(
|
|
57
|
+
modules: list[str] | None = None,
|
|
58
|
+
inventory: str | None = None,
|
|
59
|
+
secrets: list[str] | None = None,
|
|
60
|
+
secret_bindings: dict[str, dict[str, str]] | None = None,
|
|
61
|
+
check_mode: bool = False,
|
|
62
|
+
verbose: bool = False,
|
|
63
|
+
quiet: bool = False,
|
|
64
|
+
on_event: EventCallback | None = None,
|
|
65
|
+
fail_fast: bool = False,
|
|
66
|
+
print_summary: bool = True,
|
|
67
|
+
print_errors: bool = True,
|
|
68
|
+
auto_install_deps: bool = False,
|
|
69
|
+
record_deps: bool = False,
|
|
70
|
+
deps_file: str = ".ftl2-deps.txt",
|
|
71
|
+
modules_file: str = ".ftl2-modules.txt",
|
|
72
|
+
gate_modules: "list[str] | str | None" = None,
|
|
73
|
+
gate_subsystem: bool = False,
|
|
74
|
+
state_file: str | None = ".ftl2-state.json",
|
|
75
|
+
record: str | None = None,
|
|
76
|
+
replay: str | None = None,
|
|
77
|
+
vault_secrets: dict[str, str] | None = None,
|
|
78
|
+
policy: str | None = None,
|
|
79
|
+
environment: str = "",
|
|
80
|
+
) -> AsyncGenerator[AutomationContext, None]:
|
|
81
|
+
"""Create an automation context for running FTL modules.
|
|
82
|
+
|
|
83
|
+
This is the main entry point for automation scripts. It provides
|
|
84
|
+
a clean interface where modules are accessed as attributes:
|
|
85
|
+
|
|
86
|
+
async with automation() as ftl:
|
|
87
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
modules: List of module names to enable. If None, all modules
|
|
91
|
+
are available. Use this to restrict which modules can
|
|
92
|
+
be called (e.g., for safety or documentation).
|
|
93
|
+
inventory: Path to inventory file, or None for localhost only.
|
|
94
|
+
Enables ftl.hosts access and ftl.run_on() for remote
|
|
95
|
+
execution.
|
|
96
|
+
secrets: List of environment variable names to load as secrets.
|
|
97
|
+
Access via ftl.secrets["NAME"]. Values are never logged.
|
|
98
|
+
secret_bindings: Automatic secret injection for modules. Maps module
|
|
99
|
+
patterns to {param: env_var} bindings. Secrets are injected
|
|
100
|
+
automatically so scripts never see actual values:
|
|
101
|
+
{"community.general.slack": {"token": "SLACK_TOKEN"}}
|
|
102
|
+
check_mode: Enable dry-run mode. Modules will report what they
|
|
103
|
+
would change without making actual changes.
|
|
104
|
+
verbose: Enable verbose output showing each module execution,
|
|
105
|
+
including timing information.
|
|
106
|
+
quiet: Suppress all output (overrides verbose). Useful for scripts
|
|
107
|
+
where you only want to check ftl.results programmatically.
|
|
108
|
+
on_event: Callback for structured events. Receives dict with keys:
|
|
109
|
+
event ("module_start" or "module_complete"), module, host,
|
|
110
|
+
timestamp, and event-specific data (success, changed, duration).
|
|
111
|
+
fail_fast: Stop execution on first error. Raises AutomationError
|
|
112
|
+
immediately when a module fails. Default is False (continue
|
|
113
|
+
and collect errors in ftl.errors).
|
|
114
|
+
print_summary: Print per-host summary on context exit. Default is True.
|
|
115
|
+
Shows counts of changed/ok/failed tasks per host.
|
|
116
|
+
print_errors: Print error summary on context exit. Default is True.
|
|
117
|
+
Set to False to handle errors manually via ftl.errors.
|
|
118
|
+
auto_install_deps: Automatically install missing Python dependencies
|
|
119
|
+
using uv when an Ansible module requires packages
|
|
120
|
+
that aren't installed. Default is False.
|
|
121
|
+
record_deps: Record module dependencies during execution and write
|
|
122
|
+
to deps_file on context exit. Use with auto_install_deps
|
|
123
|
+
for development to capture all needed packages.
|
|
124
|
+
deps_file: Path to write recorded dependencies. Default is
|
|
125
|
+
".ftl2-deps.txt". Only used when record_deps=True.
|
|
126
|
+
modules_file: Path to write recorded module names. Default is
|
|
127
|
+
".ftl2-modules.txt". Only used when record_deps=True.
|
|
128
|
+
gate_modules: Modules to bake into the gate for remote execution.
|
|
129
|
+
Accepts a list of module names, "auto" to read from
|
|
130
|
+
modules_file (or record on first run), or None for
|
|
131
|
+
per-task module transfer (default).
|
|
132
|
+
gate_subsystem: Register the gate as an SSH subsystem on remote
|
|
133
|
+
hosts. Requires root. Eliminates shell startup
|
|
134
|
+
overhead on subsequent connections. Default False.
|
|
135
|
+
state_file: Path to state file for persistent host/resource tracking.
|
|
136
|
+
When enabled, add_host() persists to state file immediately,
|
|
137
|
+
and hosts are loaded from state on context enter. Enables
|
|
138
|
+
crash recovery and idempotent provisioning. Default is
|
|
139
|
+
".ftl2-state.json". Pass None to disable.
|
|
140
|
+
record: Path to JSON file for recording all actions as an audit
|
|
141
|
+
trail. Written on context exit with timestamps, durations,
|
|
142
|
+
parameters (excluding secrets), and results. Default is None.
|
|
143
|
+
replay: Path to a previous audit recording JSON file. When provided,
|
|
144
|
+
successful actions are skipped (returning cached output) and
|
|
145
|
+
execution resumes from the first unmatched or failed action.
|
|
146
|
+
Matching is positional. Use with record= to write a new audit
|
|
147
|
+
log including both replayed and newly executed actions.
|
|
148
|
+
vault_secrets: Mapping of secret names to HashiCorp Vault KV v2
|
|
149
|
+
references in "path#field" format. Secrets are read from Vault
|
|
150
|
+
at startup and accessible via ftl.secrets["NAME"]. Requires
|
|
151
|
+
VAULT_ADDR and VAULT_TOKEN environment variables. Example:
|
|
152
|
+
vault_secrets={"DB_PW": "myapp#db_password"}
|
|
153
|
+
Can also be referenced in secret_bindings for auto-injection.
|
|
154
|
+
policy: Path to a YAML policy file. When provided, every module
|
|
155
|
+
execution is checked against policy rules before running.
|
|
156
|
+
A matching deny rule raises PolicyDeniedError.
|
|
157
|
+
environment: Environment label for policy matching (e.g., "prod",
|
|
158
|
+
"staging"). Used by policy rules with environment conditions.
|
|
159
|
+
|
|
160
|
+
Yields:
|
|
161
|
+
AutomationContext with ftl.module_name() access to all modules
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
AutomationError: If fail_fast=True and a module fails
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
# Basic usage (localhost)
|
|
168
|
+
async with automation() as ftl:
|
|
169
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
170
|
+
await ftl.command(cmd="echo hello")
|
|
171
|
+
|
|
172
|
+
# With inventory for remote execution
|
|
173
|
+
async with automation(inventory="hosts.yml") as ftl:
|
|
174
|
+
# Local execution
|
|
175
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
176
|
+
|
|
177
|
+
# Remote execution on hosts/groups
|
|
178
|
+
await ftl.run_on("webservers", "file", path="/var/www", state="directory")
|
|
179
|
+
await ftl.run_on(ftl.hosts["db01"], "command", cmd="pg_dump mydb")
|
|
180
|
+
|
|
181
|
+
# With secrets
|
|
182
|
+
async with automation(secrets=["AWS_ACCESS_KEY_ID", "API_TOKEN"]) as ftl:
|
|
183
|
+
key = ftl.secrets["AWS_ACCESS_KEY_ID"] # Get value
|
|
184
|
+
if "API_TOKEN" in ftl.secrets: # Check exists
|
|
185
|
+
token = ftl.secrets["API_TOKEN"]
|
|
186
|
+
|
|
187
|
+
# Restricted modules
|
|
188
|
+
async with automation(modules=["file", "copy"]) as ftl:
|
|
189
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
190
|
+
await ftl.command(cmd="echo") # Raises AttributeError
|
|
191
|
+
|
|
192
|
+
# Check mode (dry run)
|
|
193
|
+
async with automation(check_mode=True) as ftl:
|
|
194
|
+
await ftl.file(path="/tmp/test", state="absent")
|
|
195
|
+
# Reports what would be deleted without deleting
|
|
196
|
+
|
|
197
|
+
# Verbose output with timing
|
|
198
|
+
async with automation(verbose=True) as ftl:
|
|
199
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
200
|
+
# Prints: [file] ok (changed) (0.02s)
|
|
201
|
+
|
|
202
|
+
# Quiet mode for scripts
|
|
203
|
+
async with automation(quiet=True) as ftl:
|
|
204
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
205
|
+
# No output, check ftl.results for status
|
|
206
|
+
|
|
207
|
+
# Event callback for custom handling
|
|
208
|
+
events = []
|
|
209
|
+
async with automation(on_event=events.append) as ftl:
|
|
210
|
+
await ftl.file(path="/tmp/test", state="touch")
|
|
211
|
+
print(f"Collected {len(events)} events")
|
|
212
|
+
|
|
213
|
+
# Error handling - collect and inspect
|
|
214
|
+
async with automation() as ftl:
|
|
215
|
+
await ftl.file(path="/nonexistent/path", state="touch") # May fail
|
|
216
|
+
await ftl.file(path="/tmp/test", state="touch") # Still runs
|
|
217
|
+
|
|
218
|
+
if ftl.failed:
|
|
219
|
+
for error in ftl.errors:
|
|
220
|
+
print(f"Error in {error.module}: {error.error}")
|
|
221
|
+
|
|
222
|
+
# Error handling - fail fast
|
|
223
|
+
try:
|
|
224
|
+
async with automation(fail_fast=True) as ftl:
|
|
225
|
+
await ftl.file(path="/nonexistent/path", state="touch")
|
|
226
|
+
# Raises AutomationError, stops here
|
|
227
|
+
except AutomationError as e:
|
|
228
|
+
print(f"Failed: {e}")
|
|
229
|
+
|
|
230
|
+
# Secret bindings - inject secrets without script access
|
|
231
|
+
async with automation(
|
|
232
|
+
secret_bindings={
|
|
233
|
+
"community.general.slack": {"token": "SLACK_TOKEN"},
|
|
234
|
+
"amazon.aws.*": {"aws_access_key_id": "AWS_KEY"},
|
|
235
|
+
}
|
|
236
|
+
) as ftl:
|
|
237
|
+
# Token injected automatically - script never sees it
|
|
238
|
+
await ftl.community.general.slack(channel="#deploy", msg="Done!")
|
|
239
|
+
|
|
240
|
+
Note:
|
|
241
|
+
Module execution is 250x faster than subprocess-based Ansible
|
|
242
|
+
because FTL modules run in-process as Python functions.
|
|
243
|
+
"""
|
|
244
|
+
context = AutomationContext(
|
|
245
|
+
modules=modules,
|
|
246
|
+
inventory=inventory,
|
|
247
|
+
secrets=secrets,
|
|
248
|
+
secret_bindings=secret_bindings,
|
|
249
|
+
check_mode=check_mode,
|
|
250
|
+
verbose=verbose,
|
|
251
|
+
quiet=quiet,
|
|
252
|
+
on_event=on_event,
|
|
253
|
+
fail_fast=fail_fast,
|
|
254
|
+
print_summary=print_summary,
|
|
255
|
+
print_errors=print_errors,
|
|
256
|
+
auto_install_deps=auto_install_deps,
|
|
257
|
+
record_deps=record_deps,
|
|
258
|
+
deps_file=deps_file,
|
|
259
|
+
modules_file=modules_file,
|
|
260
|
+
gate_modules=gate_modules,
|
|
261
|
+
gate_subsystem=gate_subsystem,
|
|
262
|
+
state_file=state_file,
|
|
263
|
+
record=record,
|
|
264
|
+
replay=replay,
|
|
265
|
+
vault_secrets=vault_secrets,
|
|
266
|
+
policy=policy,
|
|
267
|
+
environment=environment,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
async with context:
|
|
272
|
+
yield context
|
|
273
|
+
finally:
|
|
274
|
+
# Any additional cleanup would go here
|
|
275
|
+
pass
|